bashの代替ソリューション

bashの代替ソリューション

"$@"シェルスクリプトでは、必要に応じてスクリプトパラメータを引用して拡張することがわかります。たとえば、次はスクリプトパラメータをgccに渡します。

gcc -fPIC "$@"

<<<しかし、bash pass-to-stdin構文を使用すると、"@$"期待どおりに機能しません。

#!/bin/bash
cat <<< "$@"

./test.sh foo "bar baz"指定されたようにスクリプトを呼び出します。

foo bar baz

私は希望

foo "bar baz"

シェルプロンプトで書かれたように、引数を印刷するシェルスクリプトを書く方法はありますか?例:ヒントのスクリプト・パラメーターを含む、次に使用するコマンドのヒント。

答え1

"$@"ロケーション引数ごとに1つの引数で構成されるロケーション引数のリストに展開します。

これを行うとき:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmd呼び出しは空の文字列foo barと3つのパラメータを使用して行われますblah<newline>blah。シェルはexecve()次のようにシステムコールを呼び出します。

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

同じ呼び出しを再現するためにシェルコマンドライン(つまり、シェル言語のコード)を再構成するには、次のようにします。

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

またはを使用してzshさまざまな種類の見積もりをリクエストしてください。

$ set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

またはzshbashまたはksh93(ここではbash他のシェルと一緒にYMMV)を使用してください。

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

また、シェルのxtraceオプションを使用して、シェルに実行したい内容を印刷させることもできます。

$ (PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

:上記では、位置引数を引数として no-op コマンドを実行しました。cmd私のシェルは、シェルに再入力するのに適したクールな引用方法でそれを印刷します。すべてのシェルがこれを行うわけではありません。

答え2

`"$@"` expands to the script arguments, quoting them as needed

いいえ、そうではありません。プログラムを呼び出すにはリストパラメータ、各パラメータは文字列です。シェルプログラムを実行すると、3つのパラメータを使用して./test.sh foo "bar baz"呼び出しが設定されます。 (0番目の引数はプログラム名です。これにより、プログラムは呼び出される名前がわかります。)引用は、プログラム呼び出しの機能ではなく、シェルの機能です。シェルは呼び出し時にこのリストを作成します。./test.shfoobar baz

"$@"スクリプトまたは関数に渡された引数リストを、それを使用する呼び出しの引数リストに直接コピーします。これらのリストはシェル解析されないため、引用符は含まれません。

Inでは、cat <<< "$@"単一の文字列が予想されるコンテキストで使用されます。"$@"演算子は<<<文字列リストではなく文字列を期待します。この場合、bash はリストの要素を取得し、スペースで連結します。

set -xスクリプトのデバッグの場合(閉じるために)実行すると、set +x実行前に各コマンドが印刷されるトレースモードが有効になります。 Bashではトレースが引用され、コマンドをシェルに再貼り付けることができます(すべてのsh実装でこれを行うわけではありません)。

文字列があり、それをシェルソース構文に変換して元の文字列に再度解析する場合は、それを一重引用符で囲み、文字列の各一重引用符をに置き換えることができます'\''

for x do
  printf %s "'${x//\'/\'\\\'\'}' "
done
echo

文字列置換構文は、ksh93/bash/zsh/mkshに固有のものです。通常のshでは文字列を繰り返す必要があります。

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo

答え3

"$@"必要に応じて引用してスクリプトパラメータに拡張します。

まあ。実用的な目的のために十分に近くなければならず、参考書実際にそう言った"$@" is equivalent to "$1" "$2" ...

したがって、2つの引数を使用すると、次のようfoobar bazなります。

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(引数に通常の文字列だけでなく特殊文字が含まれている場合を除き、拡張後に再展開されません$@... $1

ただし、引用符でパラメータ置換を検討しても、引用符を取得できないように引用$@符は表示されません。echogcc

<<<"$@"== ルールの例外"$1" "$2" ...であり、明示的です。言及するThe result is supplied as a single string to the command on its standard inputパラメータと変数の拡張、引用符の削除などを行った後、通常どおり入力としてのみ提供され、<<< "foo"同じfoo方法で引数としてのみsomecmd "foo"提供されますfoo

./test.sh foo "bar baz"私はスクリプトが呼び出されることを期待しています[...]foo "bar baz"

提案が維持されていても、まだでなければなりません"foo" "bar baz"。シェルまたは実行中のコマンドは、実行時にコマンドが参照される内容を知りません。あるいは、引用符がある場合でも、システムコールはnullで終わる文字列のリストを受け取り、引用符はシェル言語の機能にすぎません。他の言語には異なる規則がある場合があります。

答え4

bashの代替ソリューション

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

Bashは入れ子になった置換をサポートしていないので、ありがとうございます。https://stackoverflow.com/questions/12303974/sign-array-to-variable#12304017配列を再割り当てする方法を示すために使用されます。バラよりman bashhttps://linux.die.net/man/1/bash)配列、拡張、およびパターン置換の詳細については(パラメータ拡張の下)

分析する

Bashはコマンドライン引数を配列に入れます。$@

q引用符付き文字を保存します。

パラメータ拡張に二重引用符を使用すると、${ ... }個々のパラメータを一意の要素として保存し、それらを束ねて変数( )に配列として割り当てることができます。

/#/$q^パラメータ拡張では、パターンの先頭を引用符で囲まれた文字(正規表現など)に置き換えます。

/%/$q$パラメータ拡張では、パターンの終わりを引用符で囲まれた文字(正規表現など)に置き換えます。

ユースケース:コマンドラインからMySQLに電子メールアドレスのリストを問い合わせます。

上記のステートメントには、別の引用符文字を使用したり、パラメータ間にカンマを追加したり、最後のカンマを削除したりするなど、いくつかのバリエーションがあります。もちろん、mysqlを呼び出すときにパスワードを誤って入力しました。だから私を訴えなさい。

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END

関連情報