web-dev-qa-db-ja.com

Bashを使用するときにエスケープする必要があるのはどこの文字ですか?

Bashでエスケープする必要がある包括的な文字のリストはありますか? sedだけでチェックできますか?

特に、私は%をエスケープする必要があるかどうかをチェックしていました。私は試した

echo "h%h" | sed 's/%/i/g'

%をエスケープすることなく、うまくいきました。 %をエスケープする必要がないという意味ですか?これは必要性をチェックするための良い方法でしたか?

そしてより一般的に:それらはShellbashでエスケープするのと同じ文字ですか?

150
fedorqui

shだけでなくbashでも機能する2つの簡単で安全な規則があります。

1.文字列全体を一重引用符で囲みます

これは一重引用符を除くすべての文字に有効です。一重引用符をエスケープするには、その前の引用符を閉じ、一重引用符を挿入して、引用符をもう一度開きます。

'I'\''m a s@fe $tring which ends in newline
'

sedコマンド:sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2.すべての文字をバックスラッシュでエスケープする

これは改行を除くすべての文字に対して機能します。改行文字には一重引用符または二重引用符を使用してください。 まだ空の文字列を扱う必要があります - ""に置き換えます

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

sedコマンド:sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'

2b。もっと読みやすい2

[a-zA-Z0-9,._+:@%/-]のように簡単に安全な文字セットがありますが、読みやすくするためにエスケープしないでおくことができます。

I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"

sedコマンド:LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'


Sedプログラムでは、入力の最後の行が改行バイトで終わっているかどうかわからないことに注意してください(空の場合を除く)。そのため、上記の両方のsedコマンドでは想定していません。引用符付きの改行を手動で追加できます。

シェル変数はPOSIXの意味でテキストに対してのみ定義されていることに注意してください。バイナリデータの処理は定義されていません。重要な実装では、binaryはNULバイトを除いて動作します(変数はC文字列で実装され、C文字列、つまりプログラム引数として使用されることを意図しているため)。しかしlatin1のような "バイナリ"ロケールに切り替えるべきです。 。


shのPOSIX仕様を読むことで、規則を簡単に検証することができます。bashについては、@ AusinPhillipsでリンクされているリファレンスマニュアルを確認してください。)

206
Jo So

シェル入力として再利用できる形式

この種の要求に対しては、specialprintfフォーマットディレクティブ(%q)が作成されています。

printf [-v var] format [引数]

 %q     causes printf to output the corresponding argument
        in a format that can be reused as Shell input.

いくつかのサンプル:

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world

printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

これは変数からも使用できます。

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

全(128)ASCIIバイトのクイックチェック

128から255までのすべてのバイトをエスケープする必要があることに注意してください。

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

これは、次のようにレンダリングする必要があります。

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

最初のフィールドがバイトの16進値である場合、文字をエスケープする必要がある場合は2番目のフィールドにEが含まれ、3番目のフィールドにはエスケープされた文字表示が示されます。

なぜ,ですか?

,}{のように、always以外の文字をエスケープする必要がある場合があります。

だからalwaysではなくsometime

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

または

echo test { 1, 2, 3 }
test { 1, 2, 3 }

しかし気にする:

echo test{1,2,3}
test1 test2 test3

echo test\ {1,2,3}
test 1 test 2 test 3

echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3

echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 
37
F. Hauri

bashでRTFMを使用する必要がないようにするために、他のユーザーを保存するには、次のようにします。

文字を二重引用符で囲むと、引用符内のすべての文字のリテラル値が保持されます。ただし、$`\、および履歴展開が有効になっている場合は!は例外です。

...それであなたがそれらをエスケープすれば(そしてもちろん引用自体も)大丈夫でしょう。

あなたがもっと疑わしい「疑わしい時は、それをエスケープする」アプローチをとるならば、識別子文字(すなわちASCII文字、数字、または '_')をエスケープしないことによって特別な意味を持つ文字を代わりに取得することを避けることが可能です。これらが(つまり、いくつかの変わったPOSIX風のシェルの中で)特別な意味を持つことはあり得ないので、それを回避する必要があるということは非常にありそうもないです。

29
Matthew

print '%q'テクニック を使うと、ループを実行してどの文字が特殊であるかを見つけることができます。

#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

それはこの出力を与えます:

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

,のようないくつかの結果は少し疑わしいように見えます。これに関する@ CharlesDuffyの意見を得るのは面白いでしょう。

20
codeforester

エスケープが必要な文字は、BourneまたはPOSIXシェルではBashとは異なります。一般的に(非常に)Bashはそれらのシェルのスーパーセットなので、ShellでエスケープするものはすべてBashでエスケープする必要があります。

ニースの一般的な規則は「疑わしい場合はそれを脱出する」ということでしょう。しかし、一部の文字をエスケープすると、\nのように特別な意味があります。これらはQuotingechoの下のman bashページにリストされています。

それ以外は、英数字以外の文字はエスケープしてください。安全です。私は単一の決定的なリストを知りません。

マニュアルページには、それらがすべてどこかに記載されていますが、一箇所には記載されていません。言語を学びなさい、それが確かな方法です。

私を見つけ出したのは!です。これはBash(およびcsh)では特殊文字(履歴展開)ですが、Kornシェルではそうではありません。 echo "Hello world!"でも問題があります。通常通り一重引用符を使用すると、特別な意味がなくなります。

18
cdarke

私はあなたがbash文字列について話していると思います。エスケープするための要件が​​異なる文字列があります。例えば。一重引用符の文字列は、二重引用符の文字列とは異なります。

最も参考になるのは、bashマニュアルの 引用 セクションです。

どの文字をエスケープする必要があるかを説明します。履歴展開など、有効になっているオプションによっては、エスケープ処理が必要な文字もあります。

4
Austin Phillips

オートコンプリートを使用すると、bashが自動的に一部の文字をエスケープすることに気付きました。

たとえば、dir:Aという名前のディレクトリがある場合、bashはdir\:Aに自動補完します。

これを使用して、ASCIIテーブルの文字を使用した実験をいくつか実行し、以下のリストを導き出しました。

オートコンプリート時にbashでエスケープする文字:(スペースを含む)

 !"$&'()*,:;<=>?@[\]^`{|}

bashがエスケープしない文字

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

/はディレクトリ名には使用できないので除外しました)

3
yuri