2

シェル スクリプトから外部アプリケーションを呼び出したいのですが、このシェル スクリプトは、(他のスクリプトから) パラメーターを 1 つの変数で取得します。単一のパラメーターに二重引用符を使用する必要がなくなり、単語をスペースで区切るまで、すべて問題ありませんでした。

これが私の問題の簡単な例です(sh_paramは渡されたすべてのパラメータを出力するだけです):

#!/bin/sh

pass() {
    echo "Result with \$@"
    ./sh_param $@
    echo "Result with \"\$@\""
    ./sh_param "$@"
    echo "Result with \$*"
    ./sh_param $*
    echo "Result with \"\$*\""
    ./sh_param "$*"
}

pass '"single param" separate params'

そして結果(sh_paramは渡されたすべてのパラメータを出力するだけです):

Result with $@
Param: "single
Param: param"
Param: separate
Param: params
Result with "$@"
Param: "single param" separate params
Result with $*
Param: "single
Param: param"
Param: separate
Param: params
Result with "$*"
Param: "single param" separate params

そして私が欲しい:

Param: single param
Param: separate
Param: params
4

5 に答える 5

2

脚本

pass() {
  echo 'Result with "$@"'
  sh_param "$@"
}

sh_param() {
  for i in "$@"
  do
    echo Param: $i
  done
}

pass "single param" separate param

結果

Result with "$@"
Param: single param
Param: separate
Param: param
于 2012-05-04T13:25:20.337 に答える
1

単一の変数で立ち往生している場合は、以下を使用する必要がありますeval

$ show() { i=0; for param; do ((i++)); echo "$i>$param"; done; }

$ show '"single param" separate params'
1>"single param" separate params

$ eval show '"single param" separate params'
1>single param
2>separate
3>params

二重引用符はシェルによって食べられることに注意してください。

于 2012-05-04T15:48:18.043 に答える
1

全体を 1 つの引数として渡すことは避けてください (スクオートの場合と同様)。渡された引数の数を慎重に保持するシェル スクリプトを作成するのは非常に困難ですが、それらを文字列にフラット化し、再度展開することによって、より困難にする必要はありません。

ただし、その必要がある場合は、従うべきベスト プラクティスがいくつかあります。しばらく前に、su/sudo ラッパーとして機能するスクリプトを作成する必要がありました。su は、評価のために sh に渡す単一の引数を取ります。sudo は、変更せずに execv(e) に​​渡す任意の数の引数を取ります。

移植性を確保し、あいまいなシェルで問題が発生しないように、かなり注意する必要があります。スクリプト全体を投稿するのはあまり役に立ちませんが、一般的な要点は、いくつかのエスケープ関数を記述し、引用を実行して、安全に渡しても安全な、読み取り不可能で慎重にエスケープされた文字列を構築することevalです。

bash_escape() {
  # backtick indirection strictly necessary here: we use it to strip the
  # trailing newline from sed's output, which Solaris/BSD sed *always* output
  # (unlike GNU sed, which outputs "test": printf %s test | sed -e s/dummy//)
  out=`echo "$1" | sed -e s/\\'/\\''\\\\'\\'\\'/g`
  printf \'%s\' "$out"
}
append_bash_escape() {
  printf "%s " "$1"
  bash_escape "$2"
}
sed_escape() {
  out=`echo "$1" | sed -e 's/[\\/&]/\\\\&/g'`
  printf %s "$out"
}

これらの便利な関数を使用すると、コマンド文字列を移植可能に構築するために、次のようなことができます。

COMMAND=
while [ $# -gt 0 ] ; do
  COMMAND=`append_bash_escape "$COMMAND" "$1"` ; shift
done

次に、コマンド文字列を操作できます。たとえば、コマンド文字列を実行bash_escapeして を使用sed_escapeして文字列"su - root -c SUB_HERE"に置き換えるか、単に のような文字列に直接置き換えます"sudo -- SUB_HERE"evalこれで、メタ文字、引数の分割、エスケープされていないグロブなどを気にせずに安全にできることができます。

パラノイアになり、考えられるすべての厄介な入力でスクリプトを単体テストして、引数の分割と保持が本当に正しいことを確認してください!

于 2012-05-05T11:03:57.760 に答える
1

私自身の質問に答えます。pzanoni に感謝します。xargs は、あなたが投げているものを正しく解析しているようです:-) "$@"、"$ "、$@ および $はそれでうまく機能します。したがって、私のコードは次のようになります。

#!/bin/sh

pass() {
    echo $* | xargs ./sh_param
}

pass '"single param" separate params'

そして結果は私が望んでいたものです:

Param: single param
Param: separate
Param: params
于 2012-05-07T20:05:29.200 に答える
0

sh_paramこれはうまくいくかもしれませんが、がその引数をどのように処理するかを知らずに判断するのは困難です。

#!/bin/sh

pass() {
    echo "Result with \"\$@\""
    ./sh_param "$@"
}

pass "single param" separate params
于 2012-05-04T13:25:18.150 に答える