Bash では以下を実行します:
alias myalias='echo foo
echo bar
echo baz'
myalias
結果は次のようになります:
foo
bar
baz
しかし:
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
戻り値:
foo
なぜ?
答え1
Bash のバグを見つけたと思います。このバグはオプションに特有のものです-c
。
リモート実行は、複数行のエイリアスに関する問題とは関係ありません。ローカルの bash で試すことができます。ただし、bash スクリプトや対話型 bash では、-c
次のようにオプションを付けて試してください。
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
問題と同じ出力。 のみがfoo
印刷されます。
正しい(期待される)出力を得るには、@cuonglm が提案したように、 の後に少なくとも 1 行追加する必要がありますmyalias
。
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"
なぜこのようなことが起こるのでしょうか? 後にもう 1 行追加するとなぜmyalias
役立つのでしょうか?
これは意味をなさないと言いたいだけです。Bash のドキュメントには、このケースについて説明したり言及したりしているものはほとんどありません。このように実行することは想定されていません。これはバグです。コードを読んだら、この点がわかります。
問題のある最初のコマンドに戻ります。今回は何も変更せず、bashを再コンパイルするだけです。「ONESHOT」未定義すると、正しい(期待通りの)出力が得られます。はい、おっしゃる通り、コンパイル時の設定が異なるため、コマンドには 2 つの異なる動作があります。
定義の有無によって、ONESHOT
Bash コードに 2 つのまったく異なるルートが生まれます-c "command"
。ONESHOT を定義解除すると、-c "command"
対話型コマンドや bash スクリプトなど、ほとんどすべての bash 実行のコード ルートである通常のコード ルートが実行されます。ただし、ONESHOT を定義すると、-c "command"
フォークを回避することでパフォーマンスを向上させるために、専用に設計された別の特定のルートが実行されます。
この場合、通常よく使用される方法では正しい出力が得られますが、特定の方法では正しく出力されません。この一貫性のない動作は、Bash の作者が望んでいるものではないと思います。どちらの動作が正しいかという点については、通常の方法が正しいと思います。
このバグに関する詳細
次のコードはバグに関連しています。これは、ファイルbuiltins/evalstring.cの関数parse_and_execute()からのものです。
while (*(bash_input.location.string))
{
...
}
このwhile
ループは行単位で実行され、1 回のループで 1 行を処理します。myalias
コマンドの最後の行 (上記を参照) の read の後、 の条件はwhile
false になります。myalias
は 3 行の echo に展開されますが、このループで処理される echo は 1 つだけです。他の 2 つの echo は次のループで処理されますが、別のループはありません。
の後にもう 1 行追加するとmyalias
、 read の後にmyalias
の条件はwhile
true のままになり、他の 2 つの echo が次のループで実行される機会が得られます。 の後の最後の行は、myalias
によって展開されたすべての echo が処理された後に処理されますmyalias
。
アップデート
この問題に関係するBashのバージョンを言うのを忘れましたが、
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
答え2
回避策 (@cuonglm からヒントを得た):
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"
これにより終了コードが保持されます。true
ただし、しなければならない新しい行になります。
まだ理由は説明されていません。しかし、ますますバグのように見えます。