Bash関数はどのように複数の値を返しますか?

Bash関数はどのように複数の値を返しますか?

返品のベストプラクティスは何ですか?たくさんbash機能の価値?

例1:

関数スクリプト:

function mysqlquery {
    local dbserver='localhost'
    local dbuser='user'
    local dbpass='pass'
    local db='mydb'
    mysql -h "$dbserver" -u "$dbuser" -p "$dbpass" --skip-column-names --raw -e "$*" "$db"
    if [ $? -ne 0 ]; then
        return 1
    fi
}

ソーススクリプト:

for XY in $(mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null);do
    dosomethingwith $XY
done
if mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null; then
    echo true
fi

例2:

関数スクリプト:

function mysqlquery {
    local dbserver='localhost'
    local dbuser='user'
    local dbpass='pass'
    local db='mydb'
    result=$(mysql -h "$dbserver" -u "$dbuser" -p "$dbpass" -e "$*" "$db" 2>/dev/null)
    if [ $? -ne 0 -o -z "$result" ]; then
        return 1
    fi
}

ソーススクリプト:

result=$(mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null)
for XY in $result;do
    dosomethingwith $XY
done
if mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null; then
    echo true
fi

それとも、複数の情報(単一のint値よりはるかに多くの情報)を返すより多くの方法がありますか?

ベストアンサー1

はい、数値のみを返すことbashができ、return0から255の整数のみを返すことができます。

何でも(モノのリスト)返すことができるシェルの場合は、次のようになりますes

$ es -c "fn f {return (a 'b c' d \$*)}; printf '%s\n' <={f x y}"
a
b c
d
x
y

Kornなどのシェルでは、bashいつでも事前に合意された変数でデータを返すことができます。変数は、シェルでサポートされている任意の型にすることができます。

の場合、bashスカラー希少配列(正の整数に制限されたキーを持つ連想配列)またはnull以外のキーを持つ連想配列(キーまたは値はすべてNUL文字を含めることはできません)のいずれかです。

zshこれらの制限のない一般的な配列と関連配列も参照してください。

上記の機能と同等の機能は、以下を介してf es実行できます。

f() {
  reply=(a 'b c' d "$@")
}
f
printf '%s\n' "${reply[@]}"

クエリは通常mysql、2D配列であるテーブルを返します。私が知る限り、多次元配列を持つ唯一のシェルはksh93bashたとえ変数でNUL文字をサポートしていませんが)です。

kshまたサポートします化合物タイトル付きのテーブルを便利に返す変数です。

また、参照による変数渡しもサポートしています。

したがって、次のようにすることができます。

function f {
  typeset -n var=$1
  var=(
    (foo bar baz)
    (1 2 3)
  }
}
f reply
printf '%s\n' "${reply[0][1]}" "${reply[1][2]}"

または:

function f {
  typeset -n var=$1
  var=(
    (firstname=John lastname=Smith)
    (firstname=Alice lastname=Doe)
  )
}

f reply
printf '%s\n' "${reply[0].lastname}"

出力を取得してそれをいくつかの変数に格納するには、テーブルの列をmysqlTAB文字で区切って行がNLで区切られたテキストである出力を解析し、値をいくつかのエンコードする必要があります。許可NLとタブの両方が含まれています。

それ以外の場合は、--rawNL mysqlas \n、TAB as \t、バックスラッシュas \\、NUL asが出力されます\0

ksh93read -C変数定義で書式設定されたテキストを読むことも可能なので(使用するevalものとあまり変わらないが)、次のようにすることができます。

function mysql_to_narray {
  awk -F '\t' -v q="'" '
    function quote(s) {
      gsub(/\\n/, "\n", s)
      gsub(/\\t/, "\t", s)
      gsub(/\\\\/, "\\", s)
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN{print "("}
    {
      print "("
      for (i = 1; i <= NF; i++)
        print " " quote($i)
      print ")"
    }
    END {print ")"}'
}

function query {
  typeset -n var=$1
  typeset db=$2
  shift 2

  typeset -i n=0
  typeset IFS=' '
  typeset credentials=/path/to/file.my # not password on the command line!
  set -o pipefail

  mysql --defaults-extra-file="$credentials" --batch \
        --skip-column-names -e "$*" "$db" |
    mysql_to_narray |
    read -C var
}

次のように使用

query myvar mydb 'select * from mytable' || exit
printf '%s\n' "${myvar[0][0]}"...

または複合変数の場合:

function mysql_to_array_of_compounds {
  awk -F '\t' -v q="'" '
    function quote(s) {
      gsub(/\\n/, "\n", s)
      gsub(/\\t/, "\t", s)
      gsub(/\\\\/, "\\", s)
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN{print "("}
    NR == 1 {
      for (i = 1; i<= NF; i++) header[i] = $i
      next
    }
    {
      print "("
      for (i = 1; i <= NF; i++)
        print " " header[i] "=" quote($i)
      print ")"
    }
    END {print ")"}'
}

function query {
  typeset -n var=$1
  typeset db=$2
  shift 2

  typeset -i n=0
  typeset IFS=' '
  typeset credentials=/path/to/file.my # not password on the command line!
  set -o pipefail

  mysql --defaults-extra-file="$credentials" --batch \
        -e "$*" "$db" |
    mysql_to_array_of_compounds |
    read -C var
}

次のように使用されます。

query myvar mydb 'select "First Name" as firstname, 
                         "Last Name" as lastname from mytable' || exit

printf '%s\n' "${myvar[0].firstname"

ヘッダー名(firstname上記lastname)は有効なシェル識別子でなければなりません。

あるいは、(zshとyashの配列インデックスは1から始まり、NUL文字のみを格納できますが)、列ごとに1つの配列を返すコードを生成することで常に定義bashできzshます。yashzshawk

query() {
  typeset db="$1"
  shift

  typeset IFS=' '
  typeset credentials=/path/to/file.my # not password on the command line!
  set -o pipefail

  typeset output
  output=$(
    mysql --defaults-extra-file="$credentials" --batch \
          -e "$*" "$db" |
      awk -F '\t' -v q="'" '
        function quote(s) {
          gsub(/\\n/, "\n", s)
          gsub(/\\t/, "\t", s)
          gsub(/\\\\/, "\\", s)
          gsub(q, q "\\" q q, s)
          return q s q
        }
        NR == 1 {
          for (n = 1; n<= NF; n++) column[n] = $n "=("
          next
        }
        {
          for (i = 1; i < n; i++)
            column[i] = column[i] " " quote($i)
        }
        END {
          for (i = 1; i < n; i++)
            print column[i] ") "
        }'
  ) || return
  eval "$output"
}

次のように使用されます。

query mydb 'select "First Name" as firstname, 
                         "Last Name" as lastname from mytable' || exit

printf '%s\n' "${firstname[1]}"

メソッドと同様に、オプションをローカル関数に設定する前に、bash4.4+をset -o localoptions使用または追加してください。zshlocal -set -o pipefailksh93

上記のすべての項目では、orがブロックされている\0ため、sを実際のNULに変換しないことに注意してください。 BLOBを使用したい場合は、これを実行できますが、すべての実装で機能するわけではありません。bashksh93zshgsub(/\\0/, "\0", s)awk

とにかく、ここではこの種の操作を実行するためにシェルよりも高いレベルの言語(PerlやPythonなど)を使用します。

おすすめ記事