web-dev-qa-db-ja.com

なぜ `Sudo -i`が` Sudo`ではなく改行文字を削除するのですか?

以下の例を説明するにはどうすればよいですか?また、できればこれを修正するにはどうすればよいですか? $nを使用して、複数の行コマンド文字列をシミュレートしています。これは、本当の質問から注意をそらすためです。

~$ n=$'\n'; Sudo -i echo "line1${n}line2${n}"
line1line2
~$

だが

~$ n=$'\n'; Sudo echo "line1${n}line2${n}"
line1
line2

~$
11
argle

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はそれを正式に出力します。


ここでの考えは、Sudosh -csingle文字列を受け取るという事実に対応するために、エスケープするシェルのレイヤーを追加しようとする一方で、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はそれを考慮していないようです。

いずれにしても、このような引用の問題は、必要なコマンドをファイルに保存し、それをスクリプトとして実行すると、おそらくかなり簡単になります。

11
ilkkachu

コマンドの構造を変更できます:

echo "echo \"line1${n}line2${n}\"" | Sudo -i bash -s

このようにSudoは引数を認識しないため、それを台無しにすることはできません。

2
Hauke Laging