回答:
"$@"すべての引数を表すために使用します。
for var in "$@"
do
echo "$var"
done
これにより、各引数が繰り返され、別の行に出力されます。$ @は$ *のように動作しますが、引用符で囲まれている場合、引数にスペースがあると引数が適切に分割されます。
sh test.sh 1 2 '3 4'
1
2
3 4
"$@"位置パラメータがない場合は何も展開せず"$*"、空の文字列に展開されることです。そして、はい、引数なしと1つの空の引数の間には違いがあります。${1:+"$@"} in / bin / sh`を参照してください。
$varすると、引数を実行できました。
shiftを破棄$1し、後続のすべての要素をシフトダウンします。
ロバート・ギャンブルの簡潔な答えは、質問を直接扱います。これは、スペースを含むファイル名に関するいくつかの問題を増幅します。
基本的な論文: "$@"正しい、そして$*(引用符で囲まれていない)ほとんどの場合間違っている。これは"$@"、引数にスペースが含まれている場合は正常に機能し、含まれ$*ていない場合と同様に機能するためです。状況によって"$*"は問題ありませんが、"$@"通常は(常にではありませんが)同じ場所で機能します。引用符で囲まれていない、$@と$*同等の(とほとんど常に間違っている)です。
だから、何の違いは$*、$@、"$*"、と"$@"?それらはすべて「シェルへのすべての引数」に関連していますが、実行する処理は異なります。引用符で囲まれ$*て$@いない場合、同じことを行います。彼らはそれぞれの「単語」(空白以外のシーケンス)を個別の引数として扱います。ただし、引用符で囲まれた形式はまったく異なります。"$*"引数リストはスペースで区切られた単一の文字列"$@"として扱われますが、引数はコマンドラインで指定されたときとほとんど同じように扱われます。
"$@"位置引数がない場合、何も展開しません。"$*"空の文字列に展開されます—そうです、違いがありますが、それを理解するのは難しいかもしれません。(非標準)コマンドの導入後、以下の詳細情報を参照してくださいal。
副論文:引数をスペースで処理してから他のコマンドに渡す必要がある場合は、支援するために非標準のツールが必要になることがあります。(または、配列を慎重に使用する必要があります。"${array[@]}"動作はと同様"$@"です。)
例:
$ mkdir "my dir" anotherdir
$ ls
anotherdir my dir
$ cp /dev/null "my dir/my file"
$ cp /dev/null "anotherdir/myfile"
$ ls -Fltr
total 0
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir/
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir/
$ ls -Fltr *
my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ ls -Fltr "./my dir" "./anotherdir"
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ var='"./my dir" "./anotherdir"' && echo $var
"./my dir" "./anotherdir"
$ ls -Fltr $var
ls: "./anotherdir": No such file or directory
ls: "./my: No such file or directory
ls: dir": No such file or directory
$
それがうまくいかないのはなぜですか?シェルは変数を展開する前に引用符を処理するため、機能しません。したがって、シェルにに埋め込まれた引用符に注意を向けさせるには、以下$varを使用する必要がありますeval。
$ eval ls -Fltr $var
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$
" He said,
"Don't do this!""(引用符と二重引用符とスペースを含む)などのファイル名がある場合、これは本当にトリッキーになります。
$ cp /dev/null "He said, \"Don't do this!\""
$ ls
He said, "Don't do this!" anotherdir my dir
$ ls -l
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 15:54 He said, "Don't do this!"
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir
$
シェル(それらすべて)は、そのようなものを処理することを特に簡単にはしません。そのため、(おかしなことに)多くのUnixプログラムは、それらをうまく処理できません。Unixでは、ファイル名(単一のコンポーネント)には、スラッシュとNULを除く任意の文字を含めることができます'\0'。ただし、シェルでは、パス名のどこにもスペースや改行、タブを使用しないでください。標準のUnixファイル名にスペースなどが含まれていないのもこのためです。
スペースやその他の厄介な文字が含まれている可能性のあるファイル名を処理するときは、非常に注意する必要があります。また、Unixでは標準的でないプログラムが必要であることがかなり前にわかりました。私はそれを呼び出しますescape(バージョン1.1の日付は1989-08-23T16:01:45Z)。
escapeSCCS制御システムでの使用例を以下に示します。delta(チェックインを考える)とget(チェックアウトを考える)の
両方を実行するカバースクリプトです。特に-y(変更を加えた理由)さまざまな引数には、空白と改行が含まれます。このスクリプトは1992年から$(cmd ...)使用さ#!/bin/shれているため、表記ではなくバックティックを使用し、最初の行では使用しないことに注意してください
。
: "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
# Delta and get files
# Uses escape to allow for all weird combinations of quotes in arguments
case `basename $0 .sh` in
deledit) eflag="-e";;
esac
sflag="-s"
for arg in "$@"
do
case "$arg" in
-r*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
-e) gargs="$gargs `escape \"$arg\"`"
sflag=""
eflag=""
;;
-*) dargs="$dargs `escape \"$arg\"`"
;;
*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
esac
done
eval delta "$dargs" && eval get $eflag $sflag "$gargs"
(私はおそらく最近、エスケープをそれほど徹底的に使用しないでしょう- -eたとえば、引数では必要ありません-しかし、全体として、これはを使用する私の最も簡単なスクリプトの1つですescape。)
escapeプログラムは、単に、むしろのように、その引数を出力しecho
ませんが、それは引数がで使用するために保護されることが保証されます
eval(1つのレベルeval、私はリモートシェルの実行をしたプログラムを持っている、との出力をエスケープする必要があることescape)。
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
al引数を1行に1つリストする別のプログラムがあります(それはさらに古いものです:バージョン1.1、日付は1987-01-27T14:35:49)。コマンドにプラグインして、実際にコマンドに渡される引数を確認できるため、スクリプトのデバッグ時に最も役立ちます。
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[ 追加:
次に、さまざまな"$@"表記法の違いを示すために、もう1つの例を示します。
$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh * */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$
コマンドラインで*との間の元の空白を保持するものがないことに注意してください*/*。また、次のコマンドを使用して、シェルの「コマンドライン引数」を変更できることに注意してください。
set -- -new -opt and "arg with space"
これにより、「-new」、「-opt」、「and」、「」の4つのオプションが設定されますarg with space。
]
うーん、それはかなり長い答えです -おそらく解釈はより良い言葉です。escapeリクエストに応じて利用可能なソースコード(Gmail dot comのfirstname dot lastnameへのメール)。のソースコードalは信じられないほど単純です:
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
それで全部です。これはtest.sh、Robert Gambleが示したスクリプトに相当し、シェル関数として書くことができます(ただし、最初に書いたとき、シェルバージョンはローカルバージョンのBourneシェルには存在しませんでしたal)。
またal、単純なシェルスクリプトとして記述できることにも注意してください。
[ $# != 0 ] && printf "%s\n" "$@"
条件が必要なのは、引数が渡されない場合に出力を生成しないためです。printfコマンドは、フォーマット文字列引数で空白行を生成しますが、Cプログラムでは、何も生成しません。
ロバートの答えは正しいことに注意してくださいsh。あなたは(移植性のある)それをさらに簡単にすることができます:
for i in "$@"
以下と同等です。
for i
つまり、何も必要ありません。
テスト($コマンドプロンプト):
$ set a b "spaces here" d
$ for i; do echo "$i"; done
a
b
spaces here
d
$ for i in "$@"; do echo "$i"; done
a
b
spaces here
d
KernighanとPikeによるUnixプログラミング環境でこれについて最初に読みました。
で、これbashをhelp for文書化します:
for NAME [in WORDS ... ;] do COMMANDS; done
'in WORDS ...;'が存在しない場合は、'in "$@"'想定されます。
"$@"意味が何であるかを知る必要があります。いったんfor i意味がわかると、それはと同じくらい読みやすくなりfor i in "$@"ます。
for iそれはキーストロークを節約するので、私はそれがより良いと断言しませんでした。私はの読みやすさを比較for iしてfor i in "$@"。
単純なケースでは、も使用できますshift。引数リストをキューのように扱います。それぞれshiftが最初の引数をスローし、残りの各引数のインデックスがデクリメントされます。
#this prints all arguments
while test $# -gt 0
do
echo "$1"
shift
done
shiftforループで実行した場合、その要素はまだ配列の一部であるので、この答えはより良いと思いますが、これはshift期待どおりに機能します。
echo "$1"して、引数文字列の値の間隔を維持する必要があることに注意してください。また、(マイナーな問題)これは破壊的です。引数をすべて移動すると、引数を再利用できなくなります。多くの場合、それは問題ではありません。とにかく一度だけ引数リストを処理します。
--file myfile.txt 、$ 1がパラメーター、$ 2が値であり、別の引数をスキップする必要がある場合は、shiftを2回呼び出します
たとえば、すべての要素を反復処理しない場合は、配列要素としてそれらにアクセスすることもできます。
argc=$#
argv=("$@")
for (( j=0; j<argc; j++ )); do
echo "${argv[j]}"
done
./my-script.sh param1 "param2 is a quoted param"-argv =($ @)を使用すると[param1、param2]が取得されます
(特定の単語を検索するなどのために)引数リストをインデックスで列挙する必要がある場合は、bazの答えを増幅して、リストをコピーしたり変更したりせずにこれを行うことができます。
引数リストを2つのダッシュ( "-")で分割し、ダッシュの前の引数を1つのコマンドに渡し、ダッシュの後の引数を別のコマンドに渡したいとします。
toolwrapper() {
for i in $(seq 1 $#); do
[[ "${!i}" == "--" ]] && break
done || return $? # returns error status if we don't "break"
echo "dashes at $i"
echo "Before dashes: ${@:1:i-1}"
echo "After dashes: ${@:i+1:$#}"
}
結果は次のようになります。
$ toolwrapper args for first tool -- and these are for the second
dashes at 5
Before dashes: args for first tool
After dashes: and these are for the second
getoptスクリプトでコマンドを使用して、コマンドラインオプションまたはパラメーターをフォーマットします。
#!/bin/bash
# Extract command line options & values with getopt
#
set -- $(getopt -q ab:cd "$@")
#
echo
while [ -n "$1" ]
do
case "$1" in
-a) echo "Found the -a option" ;;
-b) param="$2"
echo "Found the -b option, with parameter value $param"
shift ;;
-c) echo "Found the -c option" ;;
--) shift
break ;;
*) echo "$1 is not an option";;
esac
shift