$*と$@の違いは何ですか?

$*と$@の違いは何ですか?

次のコードを考えてみましょう。

foo () {
    echo $*
}

bar () {
    echo $@
}

foo 1 2 3 4
bar 1 2 3 4

次のように出力されます。

1 2 3 4

1 2 3 4

私はKsh88を使用していますが、他の一般的なシェルにも興味があります。特定のシェルの特徴を知っていればぜひ言及してください。

Solaris の Ksh マニュアルページで次のものが見つかりました。

$ *と$ @は、引用符を使用しない場合、またはパラメータ割り当て値またはファイル名として使用する場合は同じ意味を持ちます。ただし、コマンド引数として使用される場合、$ *は "$ 1d $ 2d ..."と同じです。ここで、dはIFS変数の最初の文字で、$ @は$ 1 $ 2 ...と等価です。

変数を修正しようとしましたが、IFS出力は変更されません。私が何か間違っているのではないでしょうか?

ベストアンサー1

参照されず、$*同じ$@場合。パラメータにスペースやワイルドカードが含まれると、予期せず中断される可能性があるため、これらの項目を使用しないでください。


"$*"単一の単語に展開します"$1c$2c..."cはBourneシェルの空白ですが、IFS現代のBourne様シェル(kshに由来し、POSIXでshとして指定)の最初の文字なので、必要に応じて指定できます。

私が見つけた唯一の良い用途は次のとおりです。

パラメータをカンマで連結する(簡易版)

function join1 {
    typeset IFS=,      # typeset makes a local variable in ksh²
    print -r -- "$*"   # using print instead of unreliable echo³
}

join1 a b c   # => a,b,c

指定された区切り文字を使用したパラメータの連結(より良いバージョン)

function join2 {
    typeset IFS="$1"
    shift
    print -r -- "$*"
}

join2 + a b c   # => a+b+c

"$@"別の単語に拡張:"$1" "$2" ...

これはほとんど常にあなたが望むものです。各位置引数を別々の単語に拡張するので、コマンドラインまたは関数引数を取得して他のコマンドまたは関数に渡すのに理想的です。また、拡張に二重引用符を使用しているため、"$1"スペースやアスタリスク(*4を含めても問題は発生しません。


違いを説明するために3つのバージョンを作成しsvimましょうvimsudo

svim1

#!/bin/sh
sudo vim $*

svim2

#!/bin/sh
sudo vim "$*"

svim3

#!/bin/sh
sudo vim "$@"

これは、空白のない単一のファイル名などの単純な場合に機能します。

svim1 foo.txt             # == sudo vim foo.txt
svim2 foo.txt             # == sudo vim "foo.txt"
svim2 foo.txt             # == sudo vim "foo.txt"

ただし、パラメータが複数ある場合にのみ正しく機能します$*"$@"

svim1 foo.txt bar.txt     # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt     # == sudo vim "foo.txt bar.txt"   # one file name!
svim3 foo.txt bar.txt     # == sudo vim "foo.txt" "bar.txt"

そして、パラメータにスペースが含まれている場合にのみ正しく機能します"$*""$@"

svim1 "shopping list.txt" # == sudo vim shopping list.txt   # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"

したがって、この方法では"$@"常に正常に動作できます。


一部のシェルでは注意が必要ですが、マルチバイト文字では機能しません。

²typeset変数の種類と属性を設定し、変数4を変数にローカルksh変数にするために使用されます(ksh93では、function f {}Bourne構文を使用して定義された関数ではなく、Korn構文をf() ...使用して定義された関数にのみ適用されます)。つまりIFS、関数が返されると以前の値が復元されます。IFS設定が標準ではなく、一部の拡張子を引用することを忘れた場合は、後で実行するコマンドが期待どおりに機能しない可能性があるため、これは重要です。

3最初の項目がバックスラッシュで始まる場合、またはバックスラッシュが含まれている場合、引数を正しく印刷または印刷できないことがありますecho。バックスラッシュ処理に(または)オプションの区切り文字を使用せずに(または)で始まるまたは始まる引数を防ぐように指示することができます。オプションの区切り記号。標準的な選択肢になりますが、ksh88とpdksh、およびいくつかの派生物にはまだ組み込まれていません。-print-r-+---printf '%s\n' "$*"printf

4つの空白文字が含まれていない場合、"$@"Bourneシェルとksh88では正しく機能せず、$IFS実際には「引用符なし」スペースに関連付けられた位置引数として実装され、結果が分割されることに注意してください$IFS。 Bourneシェルの初期バージョンには、"$@"位置引数がない場合に空の引数に拡張されるバグがあり、これが時々このバグが表示される理由です${1+"$@"}"$@"これらのバグのどれも最新のBourne様シェルには影響しません。

5 Almquistシェルとboshそのlocal代替。bash、エイリアス(bashとzshにもあります)yashzshありますが、関数でのみ使用できることに注意してください。typesetlocaldeclarebashlocal

おすすめ記事