引用符なしでエコーを実行することは危険ですか?


11

似たようなトピックをいくつか見ましたが、それらは変数を引用しないことに言及しています。

私はこのコードを見ましたが、このコード行が実行されたときに実行するものを注入できるかどうか疑問に思っていました:

echo run after_bundle


私が持っていたときにこれに遭遇しました:target = "*** LIVE SERVER ***"; エコーターゲット:$ target; そして***😬...リストフォルダに展開
マット・パーキンス

回答:


17

特定の場合

echo run after_bundle

引用符は必要ありません。への引数echoは変数の展開やコマンドの置換などを含まない静的文字列であるため、引用符は必要ありません。これらは「2語のみ」です(Stéphaneが指摘しているように、ポータブル文字セットからさらに構築されます)。

「危険」は、シェルが展開または解釈する可能性がある変数データを処理するときに発生します。このような場合、シェルが正しいことを行い、その結果が意図したとおりになるように注意する必要があります。

次の2つの質問には、それに関する関連情報が含まれています。


echoこのサイトの回答で潜在的に有害なコマンドを「保護」するために使用されることがあります。たとえば、次のコマンドを使用して、ファイルを削除する方法、またはファイルを新しい宛先に移動する方法を示します。

echo rm "${name##*/}.txt"

または

echo mv "$name" "/new_dir/$newname"

これにより、実際にファイルを削除または名前変更する代わりに、端末にコマンドが出力されます。その後、ユーザーはコマンドを検査し、問題がないと判断し、を削除してecho再度実行します。

コマンドecho run after_bundleは、ユーザーへの指示である場合もあれば、「コメントアウトされた」コードであり、危険すぎて結果を知らずに実行できない場合もあります。

このechoように使用すると、変更されたコマンドが何をするかを知る必要があり、変更されたコマンドが実際安全であることを保証する必要があります(リダイレクトが含まれていて、パイプラインでの使用が機能しないなどの可能性ありません)。


引用符を追加することはできません十分なあなたはそれを言うことができないと同じように-しかし、シェルはどうなるのかを知ってecho rm "first file.txt" "second file.txt"から、どのような方法が異なっているecho rm "first" "file.txt" "second" "file.txt"の両方からの出力が同じであること、。シェルコマンドを出力として生成する場合は、渡されたと評価される構文の引用を再生成するか同等のものを使用する必要があります。printf '%q ' rm "first file.txt" "second file.txt"; echoargv
Charles Duffy、

@CharlesDuffy私は本当に誰もデバッグ出力をコピー&ペースト希望とシェルでそれを実行します!
クサラナンダ

1
シェルコマンドを生成してそこにパイプすることshは、まったく珍しいパターンではなく、人々が「fooコマンドラインで実行するとなぜ機能するのかと尋ねられますが、このスクリプトechoは行の前に正確な文字列を出力するのではありませんか? 」ここでいつも起こります 要するに、デバッグ出力、バグが隠されている場合は役に立ちません。バグが引用に関連している場合echoは、明らかになりません。
Charles Duffy、

27

@Kusalanandaの細かい答えに加えて、もう 1つ注意点があります

echo run after_bundle

echoシェルに特別な文字を含むように渡されたこれらの3つの引数の文字は1つもないため、問題ありません。

そして、(ここで強調したい追加のポイント)これらのバイトがシェルに特有の文字に変換されるシステムロケールはありません。

これらの文字はすべて、POSIXが移植可能な文字セットと呼ぶものに含まれています。これらの文字は、POSIXシステムのすべての文字セットに存在し、同じようにエンコードされる必要があります²。

そのため、そのコマンドラインはロケールに関係なく同じように解釈されます。

ここで、そのポータブル文字セット以外の文字を使用し始めた場合、それらがシェルにとって特別でなくても、引用符で囲むことをお勧めします。別のロケールでは、それらを構成するバイトが異なる文字として解釈される可能性があるためです。シェルに特別です。それはあなたが使っているのかecho他のコマンドを使っているのかであることに注意してください、問題はechoシェルではなくそのコードを解析する方法にあります。

たとえば、UTF-8の場合:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

これàは0xc3 0xa0としてエンコードされます。さて、シェルスクリプトにそのコード行があり、シェルスクリプトが文字セットがUTF-8ではないロケールを使用するユーザーによって呼び出された場合、これらの2バイトは非常に異なる文字になる可能性があります。

たとえば、fr_FR.ISO8859-15ロケールでは、フランス語をカバーする標準の1バイト文字セット(英語を含むほとんどの西ヨーロッパ言語で使用されるものと同じ)を使用する典型的なフランス語ロケールで、0xc3バイトはÃ文字として解釈され、0xa0は非改行スペース文字。

そして、NetBSD³のようないくつかのシステムでは、非改行スペースは空白文字と見なされ(isblank()その場合はtrueを返し、と一致します[[:blank:]])、シェルはbashそのため、構文でトークン区切り文字として扱います。

代わりに実行するのであることを意味しecho$'voil\xc3\xa0'引数として、彼らはそれを実行し$'voil\xc3'、それが印刷されないことを意味します引数としてvoilà正しく。

それは、エンコーディングと同じエンコーディングが含まれている多くの文字を持っているBIG5、BIG5-HKSCS、GB18030、GBKのような中国の文字セットとたくさん悪化し|`\マイクロソフト漢字別名、を除いて、(また、その滑稽SJIS(最悪に名前を付けます)それはの¥代わりに\ありますが\、0x5cとしてエンコードされているので、ほとんどのツールと同様に扱われます)。

たとえば、zh_CN.gb18030中国語ロケールの場合、次のようなスクリプトを記述します。

echo  reboot

このスクリプトは詜 reboot、GB18030またはGBK 唰 rebootを使用するロケール、BIG5またはBIG5-HKSCS を使用するロケールで出力しますが、ASCIIを使用するCロケールまたはISO8859-15またはUTF-8を使用するロケールではreboot、GB18030エンコーディングが実行されるため、 of は0xd4 0x7cであり、0x7cは|ASCII のエンコーディングなので、次を実行します。

 echo �| reboot

(ただし、0xd4バイトを表すことはロケールでレンダリングされます)。uname代わりに害の少ないものを使用する例reboot

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

uname実行されました)。

したがって、ポータブルキャラクタセット以外の文字を含むすべての文字列を引用することをお勧めします。

ただし、\および`のエンコーディングはこれらの文字の一部のエンコーディングに含まれているため、\or "..."または$'...'(or 内`および/または\まだ特殊)を使用せずに'...'、ポータブル文字セット外の文字を引用することをお勧めします。

'エンコーディングにのエンコーディングが含まれている(もちろん、それ自体以外の)文字セットが含まれているロケールを持つシステムは知らない'ので、これら'...'が間違いなく最も安全であるべきです。

いくつかのシェル$'\uXXXX'では、Unicodeコードポイントに基づいて文字を表現する表記法もサポートされています。zshおよびのようなシェルでbashは、文字はロケールの文字セットでエンコードされて挿入されます(ただし、その文字セットにその文字がない場合、予期しない動作が発生する可能性があります)。これにより、シェルコードに非ASCII文字を挿入する必要がなくなります。

上記のように:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

または:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(ただし、これらの文字を持たないロケールでスクリプトを実行すると、スクリプトが壊れる可能性があります)。

または、(または少なくともいくつかの実装、少なくともUnix準拠の実装)に\も特別であるため、より良い:echo echo

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

\の最初の引数でも特殊であることに注意してくださいprintf。したがって、ASCII以外の文字も、のエンコードを含む可能性がある場合に備えて避けるほうが適切です\)。

次のこともできることに注意してください:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(それはやり過ぎですが、どのキャラクターがポータブルキャラクターセットに含まれているかわからない場合は、ある程度安心できます)

また、古い`...`形式のコマンド置換(別のレベルのバックスラッシュ処理を導入する)を使用し$(...)ないでください。代わりに使用してください。


¹技術的に、echoまたに引数として渡されるechoユーティリティ(それが呼び出されたどのようにそれを伝えるために)、それはだargv[0]argcほとんどのシェルでは、今日も、3であるecho組み込みされ、その結果、exec()/bin/echoでシミュレートされた3つの引数のリストを持つファイルシェル。コマンドが主に作用する引数であるため、引数のリストを2番目の引数(argv[1]to argv[argc - 1])から始めることも一般的です。

²これの顕著な例外ja_JP.SJISは、文字セットに文字\~文字もないFreeBSDシステムの滑稽なロケールです!

many多くのシステム(FreeBSD、Solaris、GNUのものではない)はU + 00A0を[[:blank:]]UTF-8ロケールと見なしますが、ISO8859-15を使用する他のロケールでは、この種の問題を回避できるものはほとんどないことに注意してください。


あなたの最初の段落では、あなたが教えて「...文字のこれらの3つの引数が渡さにecho...」、私は、コマンドに渡される2つの引数を数えecho、私は数えることができる引数は、runafter_bundleどのようにあなたを説明するために、ケアカウントして3つの引数を得ましたか?
Ferrybig

1
@ViktorFonic、引数の数に関する編集を参照してください(主な問題はにありませんecho)。ユーティリティに(exec -a foo /bin/echo --help)任意の最初の引数を渡す方法については、GNUシステムおよびGNUシェルを参照してください/bin/echo
ステファンChazelas

@Ferrybig Stephaneの編集、脚注1を参照してください。通常のCスタイルのコマンドへの引数は、argv [0]自体が実行可能名である引数の配列です。ちょっと$0シェルの位置パラメータと似ています。
Sergiy Kolodyazhnyy

373エンコーディングがありますiconvするESCに変換されますが'。試してみてください(例として):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
NotAnUnixNazi

一部のコードポイント(ESC以外)がに変換される173のエンコーディングがあります'。試してくださいprintf '\u2804' | iconv -f utf8 -t BRF | xxd。になるコードポイントが多いエンコーディングがあります'。UCS-4の約8695のコードポイントはになり'ます。試してくださいprintf '\U627' | iconv -cf utf-8 -t UCS-4。いくつかの(37)エンコーディングは、文字0x127をに変換し'ます。試してくださいprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
NotAnUnixNazi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.