位置パラメータ付きのbash -c


15

通常、$0スクリプト内では、スクリプトの名前、またはそれが呼び出されたもの(パスを含む)に設定されます。ただし、オプションで使用bashする場合は、コマンド文字列の後に渡される引数の最初に設定されます。-c$0

bash -c 'echo $0 ' foo bar 
# foo 

実際には、位置パラメータはシフトされているように見えますが、が含まれてい$0ます。ただしshift、コマンド文字列では$0(通常どおり)影響を受けません。

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

コマンド文字列のこの奇妙な動作はなぜですか?このような奇妙な動作を実装する背後にある理由、理論的根拠を探していることに注意してください。


そのようなコマンド文字列は$0通常定義されたパラメータを必要としないと推測できるため、経済のために通常の引数にも使用されます。ただし、その場合、の動作shiftは奇妙です。別の可能性は$0、プログラムの動作を定義するために使用される(bashas shまたはvimas と呼ばれるla vi)が、$0ここではコマンド文字列でのみ表示され、その中で呼び出されるプログラムでは表示されないためです。の他の用途は考えられない$0ので、これを説明するのに途方に暮れています。


注意点として、私が使用見てきた-ため$0のように、イディオムなどの引数をsh -c 'foo $1 $2' - a b。このように、それはかなり普通に見えます(あなたがそれが何を-意味するかを知ったら、つまり)
フォルカージーゲル14

あなたもすることができますecho 'echo the other side of this pipe globs "$@"' | sh -s -- *、しかし、残念ながら、$0一般的で設定可能なパラメータではない-streamオプション...同じ方法の多くで使用することができxargs、一般的に、しかし、です。他にも。
mikeserv 14

@VolkerSiegelもっと普通だったとしたら--、他のプログラムで見られる「ここから引数を開始する」という通常の解釈ができたはずです。繰り返しますが、それは、慣れていない人が実際にその解釈を持っている-cと考えるのを混乱させる可能性があります--
ムル14

回答:


10

これにより$0、インラインスクリプトを使用するときに設定/選択する機会が与えられます。それ以外の場合は、$0単にになりますbash

次に、たとえば次のことができます。

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

すべてのシェルがそれを行うわけではありません。Bourneシェルはそうしました。Korn(およびAlmquist)シェルは、$1代わりに最初のパラメーターに移動することを選択しました。POSIXは最終的にBourne方式に移行したためkshashデリバティブは後でそれに戻りました(詳細については、http://www.in-ulm.de/~mascheck/various/find/#shellを参照)。以下のために長い時間のためにことを意味していることsh(ボーン、AlmquistまたはKornシェルに基づいたシステムによって異なります)、あなたは最初の引数が入ったかどうか知りませんでした$0か、$1そうポータビリティのために、あなたは次のようなものをしなければなりませんでした。

sh -c 'echo foo in "$1"' foo foo

または:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

ありがたいことに、POSIXは最初の引数が入る新しい動作を指定している$0ので、移植可能になりました。

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt

:私はちょうど走ったbash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txtのUbuntu 14.04、bashの上でversion 4.3.11(1)-release、そして私が得ました:txt files are *.txt。Oo
muru 14

2
@muru、これは正しいです(そして、おそらくtxt現在のディレクトリにファイルがありません)。参照bash -c 'echo "${1?}"' foo
ステファンシャゼル14

ああ、はい。これは実際に役立つ例です。
ムル14

1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt素晴らしく独創的です!
iruvar

それとも、(comp.unix.shellにステファン・Chazelasによって提案された)完全に堅牢な回避策を使用 SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... ハハ...
mikeserv

3

この動作はPOSIX定義されています:

sh -c command_name [引数...]

command_stringオペランドからコマンドを読み取ります。command_nameオペランドの値から特殊パラメーター0(特殊パラメーターを参照)の値を設定し、残りの引数オペランドから順番に位置パラメーター($ 1、$ 2など)を設定します。

この動作が必要な理由については、これにより、スクリプトと-c文字列の間のギャップが滑らかになります。動作を変更せずに、2つの間で直接変換できます。他の分野では、これらが同一であることに依存しています。

また、プログラム引数の一般的な動作方法とも一致しています。これは最終的execに、最初に提供された引数もである関数の1つを呼び出す$0ことになります。同様に、その引数は実行中の実行可能ファイルと同じです。ただし、そこに特別な値が必要な場合があり、それを取得する他の方法がない場合があります。引数が存在する場合、それは何かにマッピングする必要があり、ユーザーはそれが何であるかを設定できる必要があります。

この一貫性(およびおそらく歴史的な事故)は、あなたが見つける状況につながります。


2番目のパラの(願わくば実世界の)例を挙げてもらえますか?
ムル14

との類推に注意してくださいargv[0]。またはのような場合に$0のみにargv[0]渡されます。スクリプトの場合、引数1、2 ...としてインタープリターに指定されたパスに設定されます(直接実行されるスクリプトの場合、execve()の最初のパス引数から取得されます)。execve()shbash$0
ステファンシャゼル14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.