返品のベストプラクティスは何ですか?たくさん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
ができ、return
0から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配列であるテーブルを返します。私が知る限り、多次元配列を持つ唯一のシェルはksh93
(bash
たとえ変数で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}"
出力を取得してそれをいくつかの変数に格納するには、テーブルの列をmysql
TAB文字で区切って行がNLで区切られたテキストである出力を解析し、値をいくつかのエンコードする必要があります。許可NLとタブの両方が含まれています。
それ以外の場合は、--raw
NL mysql
as \n
、TAB as \t
、バックスラッシュas \\
、NUL asが出力されます\0
。
ksh93
read -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
ます。yash
zsh
awk
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
使用または追加してください。zsh
local -
set -o pipefail
ksh93
上記のすべての項目では、orがブロックされている\0
ため、sを実際のNULに変換しないことに注意してください。 BLOBを使用したい場合は、これを実行できますが、すべての実装で機能するわけではありません。bash
ksh93
zsh
gsub(/\\0/, "\0", s)
awk
とにかく、ここではこの種の操作を実行するためにシェルよりも高いレベルの言語(PerlやPythonなど)を使用します。
答え2
まあ、必要な/必要な出力形式によって異なります。最も簡単な方法は、おそらく関数の出力を印刷して、関数が他のコマンドのように動作するようにすることです。別の方法は、関数内でいくつかの変数(おそらく連想配列)を設定することです。これの利点は、異なるプロジェクトをきれいに分離することができますが、いくつかの変数をハードコードする必要があるかもしれません。
最初の例の関数は電子を実行します。 mysql クライアントが関数から出力するものは何でも関数の標準出力に移動します。データはすでにバイトストリーム形式なので、そのままにしておくだけです。
しかし、ここで問題は出力をどのように処理するかです。for x in $(somecmd) ...
出力はsomecmd
単語に分割され、ファイル名の全体として処理されるため、それは良いことではありません。一般的に使用する方が良いですwhile read ...
。参考ファイル(データストリーム、変数)を1行ずつ(および/またはフィールドごとに)読み取る方法は?
mysql
出力を1行ずつ読み取るには:
mysql -h "$dbserver" etc. etc. | while read -r line ; do
dosomethingwith "$line"
done
または関数を使用してください
mysqlquery() {
...
mysql -h "$dbserver" etc. etc. 2>/dev/null
}
mysqlquery | while read -r line ; do ...
if [ $? -ne 0 ]; then return 1
関数の戻り値は、最後のコマンドの戻り値と同じである必要はありません。パイプラインに供給するために関数を使用している場合、戻り値を確認するのは簡単ではありません。