以下の例を説明するにはどうすればよいですか?また、できればこれを修正するにはどうすればよいですか? $n
を使用して、複数の行コマンド文字列をシミュレートしています。これは、本当の質問から注意をそらすためです。
~$ n=$'\n'; Sudo -i echo "line1${n}line2${n}"
line1line2
~$
だが
~$ n=$'\n'; Sudo echo "line1${n}line2${n}"
line1
line2
~$
Straceの下でSudo -i echo $'line1\nline2'
を実行すると、Bashが次のように開始されることがわかります。
9183 execve("/bin/bash", ["-bash", "--login", "-c", "echo line1\\\nline2\\\n"], ...
現在、strace
は、文字列を表示するときにバックスラッシュエスケープ付きの特殊文字を表示するため、Bashが実際に-c
の引数として取得するのはecho line1[backslash][newline]line2[backslash][newline]
であり、シェルの場合、バックスラッシュは行の終わりは継続行を示し、バックスラッシュとそれに続く改行を削除します。
-i
がない場合、Sudo
はシェルを経由せずにecho
を直接実行します。
9189 execve("/bin/echo", ["echo", "line1\nline2\n"], ...
ここで、それはecho
に行くリテラル改行であり、echo
はそれを正式に出力します。
ここでの考えは、Sudo
がsh -c
がsingle文字列を受け取るという事実に対応するために、エスケープするシェルのレイヤーを追加しようとする一方で、Sudo
それ自体はコマンドを個別の引数として受け取ります。
以下のケースを比較してください。
Sudo
はスペースをエスケープします(これはコマンドの名前であり、引数はありません!):
$ Sudo -i 'echo foo'
-bash: echo foo: command not found
Sudo
はバックスラッシュをエスケープして、これが実際に機能するようにします(Bashのecho
はバックスラッシュを処理しません)。
$ Sudo -i echo 'foo\bar'
foo\bar
タブと同じ:
$ Sudo -i echo $'foo\tbar'
foo bar
ここでは、バックスラッシュに余分な引用符はありません。したがって、Bashはシェルコマンドラインの処理中にそれを削除します(b
はシェルの特殊文字ではないため、引用符は必要ありません。これは基本的に同じです。 bash -c 'echo foo"b"ar'
):
$ bash -c 'echo foo\bar'
foobar
問題は、バックスラッシュで改行をエスケープできないことだけであり、Sudo
はそれを考慮していないようです。
いずれにしても、このような引用の問題は、必要なコマンドをファイルに保存し、それをスクリプトとして実行すると、おそらくかなり簡単になります。
コマンドの構造を変更できます:
echo "echo \"line1${n}line2${n}\"" | Sudo -i bash -s
このようにSudo
は引数を認識しないため、それを台無しにすることはできません。