bashシェルスクリプトですべての引数を伝播する


853

私は別のスクリプトを呼び出す非常に単純なスクリプトを書いており、現在のスクリプトから実行中のスクリプトにパラメーターを伝達する必要があります。

たとえば、私のスクリプト名はでfoo.shbar.sh

foo.sh:

bar $1 $2 $3 $4

各パラメーターを明示的に指定せずにこれを行うにはどうすればよいですか?



回答:


1408

パラメータを同じように渡したい場合"$@"は、プレーンの代わりに使用し$@ます。

観察する:

$ cat foo.sh
#!/bin/bash
baz.sh $@

$ cat bar.sh
#!/bin/bash
baz.sh "$@"

$ cat baz.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4

$ ./foo.sh first second
Received: first
Received: second
Received:
Received:

$ ./foo.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:

$ ./bar.sh first second
Received: first
Received: second
Received:
Received:

$ ./bar.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:

7
これは、引用符で囲まれた/エスケープされた文字列の純粋なパススルーで機能します。 bar2skippingディレクトリbar me真の元のコマンドラインを引用符またはエスケープ文字列で完全に表示できるシェルスクリプトを作成することは可能ですか?
Mark Edington、2013年

3
フラグを渡すのはどうですか?例: './bar.sh --with-stuff'
Nathan Long

4
ワードスプリッティングのテーマをよりよく理解したい人は、こちら
Rany Albeg Wein

4
'arg with spaces'3つの例にを含めるとどうなるかを示すこともお勧めします。結果に驚いた。うまくいけば、あなたはそれらを説明することができます。
Michael Scheper 2017年

2
@MichaelScheperは、とにかく、./foo.sh "arg with spaces"./foo.sh 'arg with spaces'私は与えられた例に追加の提案が任意の助けになるだろうか見ていないので、100%同じです。
チャールズダフィー

475

以下のためのbashや他のBourneのようなシェル:

java com.myserver.Program "$@"

13
@アミール:いいえ、cshではありません。すべての人の正気のために:cshをスクリプト化しないでください。しかし、私はおそらく$argv:qいくつかのcshバリアントで動作すると思います。
Chris Johnsen

2
ありがとう!必要に応じて、スクリプトが受け取った同じ引数のセットを渡します。1つの大きな引数ではありません。二重引用符が必要です。スペースを含む引用符付きの引数があっても機能します。
アンディトーマス

24
関連:シェルスクリプトがjavaを実行するラッパーとして機能している場合は、最後の行を作成することを検討してください。exec java com.myserver.Program "$@"これにより、bashが完了するまで待つのではなく、Javaにexecが実行されます。したがって、使用するプロセススロットが1つ少なくなります。また、親プロセス(スクリプトを実行したプロセス)がpidを介してそれを監視していて、それが「java」プロセスであると予期している場合、execを実行しないと、異常な事態が発生する可能性があります。execはjavaに同じpidを継承させます。
greggo 2014年

7
@dragonxlwang:あなたがもし、あなたのシェルがサポートし、それら(例えば、配列変数を使用することができますbashのzshの、他の人ではなく、プレーンBourne-またはPOSIXシェル):に保存する引数args=("$@")、別のシェル「単語」として、各要素を展開します(と似ている"$@""${args[@]}"
Chris Johnsen、2015年

1
を使用するときに覚えておくべき「落とし穴」はありますか?たとえば"$@"、引数内のスペース、null文字、またはその他の特殊文字をエスケープすると失敗するのですか?
IQAndreas

97

使用"$@"(すべてのための作品のPOSIX互換)。

[...]、bashには「$ @」変数があり、スペースで区切られたすべてのコマンドラインパラメータに展開されます。

例としてBashから。


6
では、「$ @」は$ @を単に引用符で囲むだけでなく、実際には別の組み込み変数ですか?
2015

5
@ben単一の変数ですが、(壊れた)とは異なる有用な値を使用するには、二重引用符で囲む必要があります$*。ここには歴史的な進展があると思います。$*設計どおりに機能しなかったため、それ$@を置き換えるために発明されました。しかし、引用ルールはそれらが何であるかであり、それを囲む二重引用符は依然として必要です(または、壊れた$*セマンティクスに戻ります)。
tripleee

7
「つまり、「$ @」は$ @を単に引用符で囲むだけでなく、実際には別の組み込み変数ですか?」-すべての目的と目的のために、はい:stackoverflow.com/a/28099707/162094
Stuart Berg

1
echo "$@"as を含むスクリプトを呼び出すと、の代わりにが./script.sh a "b c" d取得されa b c dますがa "b c" d、これは大きく異なります。
isarandi 2018年

7
@isarandi出力に引用符が含まれていないことは事実ですが、それは予想どおりです...しかし、スクリプト内でecho3つの引数を受け取るので安心してください"a" "b c" "d"(シェルはそれらを文字列展開の一部として結合します)。しかし、あなたが使っていたなら、あなたfor i in "$@"; do echo $i; doneは得たでしょうa⏎b c⏎d
FeRD

64

これはよく答えられていると思いますが、これは "$ @" $ @ "$ *"と$ *の比較です

テストスクリプトの内容:

# cat ./test.sh
#!/usr/bin/env bash
echo "================================="

echo "Quoted DOLLAR-AT"
for ARG in "$@"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-AT"
for ARG in $@; do
    echo $ARG
done

echo "================================="

echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
    echo $ARG
done

echo "================================="

次に、さまざまな引数を指定してテストスクリプトを実行します。

# ./test.sh  "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================

1
良い簡潔なデモンストレーションとこの回答への貢献。
マーリン

33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

引数がスクリプトにどのように取り込まれるかをテストするときに、これがもう少し便利かもしれないと思っただけです


6
これは間違いなく彼の質問に答える助けにはなりませんでしたが、これは確かに役に立ちます。賛成です!
Dario Russo

2
空のパラメーターが渡されると壊れます。次のことを確認する必要があります$#
Sebi

良い引数テスターは、同意""''何の引数がなかった場合も、それは沈黙している、引数として。私はこれを修正しようとしましたが、forループとでのカウンターが必要$#です。これを最後に追加しました:echo "End of args or received quoted null"
マーリン

8

私のSUN Unixには多くの制限があり、「$ @」でさえ意図したとおりに解釈されませんでした。私の回避策は$ {@}です。例えば、

#!/bin/ksh
find ./ -type f | xargs grep "${@}"

ちなみに、私のUnixもgrep -rをサポートしていないため、この特定のスクリプトが必要でした。


これはBashの質問です。あなたが使っているksh
tripleee

6

含める場合 $@引用符で囲まれた文字列に他の文字、複数の引数がある場合の動作は非常に奇妙であり、最初の引数のみが引用符内に含まれます。

例:

#!/bin/bash
set -x
bash -c "true foo $@"

収量:

$ bash test.sh bar baz
+ bash -c 'true foo bar' baz

しかし、最初に別の変数に割り当てる:

#!/bin/bash
set -x
args="$@"
bash -c "true foo $args"

収量:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'

3
それが当惑することを否定するつもりはありませんが、実際には"$@"、bashのセマンティクスの範囲内で理にかなっています。また、との主な違い$@$*、両方がなぜ有用であるかを説明するのにも役立ちます。bash(1)manページ特殊パラメータセクション:「*-ダブルクォートの内部で展開が行われた場合、それは各パラメータの値を持つ単一の単語に展開されます[...]である、"$*"と等価である"$1c$2c..."場合は、c[あります$IFS]。」そして実際、最初の例の$*代わりにを使用$@すると、2番目のバージョンと同じ出力が得られます。
FeRD 2018

2
それをと比較してください"$@"。再びmanページから: " @—二重引用符内で展開が行われると、各パラメーターは個別の単語に展開されます。つまり、… "$@"と同じ"$1" "$2"です。二重引用符で囲まれた展開が単語内で発生すると、最初のパラメーターの展開が結合されます元の単語の最初の部分で、最後のパラメータの展開が元の単語の最後の部分と結合されます。」...そして実際、もしあなたのコードがそうだったならbash -c "true foo $@ bar baz"、それtest.sh one twoをnetのように実行しbash -c 'true foo one' 'two bar baz'ます。
FeRD

1
ドキュメントの引用やに関する情報のためのおかげで$*、私はそれが存在していることを忘れているようだ...
ColinM

へえ。私は反対です。$@私が最初にシェルスクリプトを始めたとき、ちょうど引き付けられていました。それがまだあることを思い出さなければなりません。"$*"スクリプトで使用されるのが一般的でした...それから著者はそれがすべての引数を一緒に壊していることに気づくでしょう、それで彼らは複雑なナンセンスのすべての方法を単語分割"$*"で試すか、ループすることによって引数リストを[再]組み立てますオーバーshiftだけ使用して...一つ一つ下のそれらを引っ張って$@解きにそれを。(bashが同じニーモニックを使用して配列のメンバーにアクセスすることも支援します。${var[*]}それらはすべて単語として、単語${var[@]}のリストとして。)
FeRD

ここでの実際の問題は、bash -cまったく意味のない方法で使用していることです。
3

4

時には、あなたは(例えば、すべての引数を渡したいが、フラグが先行しました--flag

$ bar --flag "$1" --flag "$2" --flag "$3"

これは次の方法で実行できます。

$ bar $(printf -- ' --flag "%s"' "$@")

注:余分なフィールド分割を避けるために、あなたが引用しなければならない%s$@し、単一の文字列を避けるために、あなたはのサブシェルを引用することはできませんprintf


3

スペースやエスケープ文字がある場合を除いて、正常に機能します。この場合、引数をキャプチャしてスクリプト内のSSHに送信する方法が見つかりません。

これは便利かもしれませんがとても醜いです

_command_opts=$( echo "$@" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&@",$i) ; gsub(/ $/,"",$i );gsub (/$/,"@",$i) }; print $0 }' | tr '@' \' )

3

ここでは多くの回答が推奨されている$@$*、または引用符が付いていないかを示していますが、これらが実際に何をするのか、なぜそうすべきなのかを説明している人はいません。だから私はこの答えからこの素晴らしい要約を盗みます

ここに画像の説明を入力してください

引用符はすべての違いを生むことに注意してください。引用符がないと、どちらも同じ動作になります。

私の目的のために、あるスクリプトから別のスクリプトにパラメーターをそのまま渡す必要があり、そのための最良のオプションは次のとおりです。

# file: parent.sh
# we have some params passed to parent.sh 
# which we will like to pass on to child.sh as-is

./child.sh $*

引用符は$@なく、上記の状況でも同様に機能することに注意してください。


2

bar "$@" と同等になります bar "$1" "$2" "$3" "$4"

引用符が重要であることに注意してください!

"$@"$@"$*"または$*この中に記載されるようにエスケープおよび連結についてなる各挙動わずかに異なります stackoverflowの答えが

密接に関連する使用例の1つは 、次のような引数内のすべての指定された引数を渡すことです。

bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\""

私はこれを達成するために@kvantourの答えのバリエーションを使用します:

bash -c "bar $(printf -- '"%s" ' "$@")"

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.