GNU/Linux >> Linux の 問題 >  >> Linux

シェル変数を引用符で囲むのはいつですか?

一般的な規則:空であるか、スペース (または実際には空白) または特殊文字 (ワイルドカード) を含むことができる場合は、それを引用します。文字列をスペースで引用しないと、多くの場合、シェルが 1 つの引数を複数に分割してしまいます。

$? 数値なので引用符は必要ありません。 $URLかどうか 何が必要かは、そこで許可するものと、空の場合でも引数が必要かどうかによって異なります。

私は常に習慣から文字列を引用する傾向があります。その方が安全だからです.


つまり、トークンの分割とワイルドカードの展開を実行するためにシェルを必要としないすべての箇所を引用してください。

一重引用符は、それらの間のテキストをそのまま保護します。シェルが弦にまったく触れないようにする必要がある場合に適したツールです。通常、変数補間を必要としない場合に選択される引用メカニズムです。

$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change

$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.

変数の補間が必要な場合は、二重引用符が適しています。適切に調整すれば、文字列内に一重引用符が必要な場合にも有効な回避策になります。 (一重引用符の間に一重引用符をエスケープする簡単な方法はありません。一重引用符の中にエスケープ メカニズムがないためです。もしあったとしても、完全にそのまま引用することはできません。)

$ echo "There is no place like '$HOME'"
There is no place like '/home/me'

トークンの分割やワイルドカードの展開を実行するシェルを特に必要とする場合、引用符は適切ではありません。

トークン分割;

 $ words="foo bar baz"
 $ for word in $words; do
 >   echo "$word"
 > done
 foo
 bar
 baz

対照的に:

 $ for word in "$words"; do echo "$word"; done
 foo bar baz

(ループは、引用符で囲まれた単一の文字列に対して 1 回だけ実行されます。)

 $ for word in '$words'; do echo "$word"; done
 $words

(ループは、リテラルの単一引用符で囲まれた文字列に対して 1 回だけ実行されます。)

ワイルドカード展開:

$ pattern='file*.txt'
$ ls $pattern
file1.txt      file_other.txt

対照的に:

$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory

(文字通り file*.txt という名前のファイルはありません .)

$ ls '$pattern'
ls: cannot access $pattern: No such file or directory

($patternという名前のファイルはありません 、いずれか!)

より具体的には、通常、ファイル名を含むものはすべて引用符で囲む必要があります (ファイル名には空白やその他のシェル メタ文字を含めることができるため)。通常、URL を含むものはすべて引用符で囲む必要があります (多くの URL には ? のようなシェルのメタ文字が含まれているため) および & )。通常、正規表現を含むものはすべて引用符で囲む必要があります (同上)。非空白文字間の単一スペース以外の重要な空白を含むものはすべて引用符で囲む必要があります (そうしないと、シェルが空白を効果的に単一スペースに変更し、先頭または末尾の空白を削除するため)。

シェルのメタ文字を含まない値のみを変数に含めることができることがわかっている場合、引用符はオプションです。したがって、引用符で囲まれていない $? この変数には単一の数値しか含めることができないため、基本的には問題ありません。ただし、"$?" も正しく、一般的な一貫性と正確性のために推奨されます (ただし、これは私の個人的な推奨事項であり、広く認識されているポリシーではありません)。

変数ではない値は基本的に同じ規則に従いますが、メタ文字を引用する代わりにエスケープすることもできます。一般的な例として、& を含む URL メタ文字がエスケープまたは引用されていない限り、バックグラウンド コマンドとしてシェルによって解析されます:

$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found

(もちろん、これは URL が引用符で囲まれていない変数にある場合にも起こります。) 静的文字列の場合、単一引用符が最も理にかなっていますが、引用符やエスケープのどの形式でも機能します。

wget 'http://example.com/q&uack'  # Single quotes preferred for a static string
wget "http://example.com/q&uack"  # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack   # Backslash escape
wget http://example.com/q'&'uack  # Only the metacharacter really needs quoting

最後の例は、私が「シーソー引用」と呼んでいる別の便利な概念も示唆しています。一重引用符と二重引用符を混在させる必要がある場合は、それらを隣接して使用できます。たとえば、次の引用文字列

'$HOME '
"isn't"
' where `<3'
"' is."

背中合わせに貼り付けて、トークン化と引用符の削除後に単一の長い文字列を形成できます。

$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.

これは非常に判読しにくいですが、一般的な手法であるため、知っておくとよいでしょう。

余談ですが、通常、スクリプトでは ls を使用しないでください。 何のために。ワイルドカードを展開するには、... 使用してください。

$ printf '%s\n' $pattern   # not ``ls -1 $pattern''
file1.txt
file_other.txt

$ for file in $pattern; do  # definitely, definitely not ``for file in $(ls $pattern)''
>  printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt

(後者の例ではループは完全に不要です; printf 具体的には、複数の引数で正常に機能します。 stat それも。しかし、ワイルドカード マッチのループはよくある問題であり、しばしば間違って実行されます。)

ループするトークンのリストまたは展開するワイルドカードを含む変数はあまり見られないため、「何をしているのか正確にわからない限り、すべてを引用する」と略すことがあります.


一般的な引用の 3 点式は次のとおりです。

二重引用符

単語の分割とグロビングを抑制したいコンテキスト。また、リテラルを正規表現ではなく文字列として扱いたいコンテキストでも。

一重引用符

バックスラッシュの補間と特別な処理を抑制したい文字列リテラル。つまり、二重引用符の使用が不適切な状況です。

引用なし

単語の分割やグロビングの問題がないことが確実である場合、または単語の分割とグロビングが必要である場合 .

二重引用符

  • 空白を含むリテラル文字列 ("StackOverflow rocks!""Steve's Apple" )
  • 変数展開 ("$var""${arr[@]}" )
  • コマンド置換 ("$(ls)""`ls`" )
  • ディレクトリ パスまたはファイル名の一部にスペースが含まれるグロブ ("/my dir/"* )
  • 一重引用符を保護する ("single'quote'delimited'string" )
  • Bash パラメータ展開 ("${filename##*/}" )

一重引用符

  • 空白を含むコマンド名と引数
  • 補間を抑制する必要があるリテラル文字列 ( 'Really costs $$!''just a backslash followed by a t: \t' )
  • 二重引用符を保護する ('The "crux"' )
  • 補間を抑制する必要がある正規表現リテラル
  • 特殊文字 ($'\n\t') を含むリテラルには、シェルの引用符を使用します )
  • いくつかの一重引用符と二重引用符を保護する必要がある場合は、シェルの引用符を使用します ($'{"table": "users", "where": "first_name"=\'Steve\'}' )

引用なし

  • 標準数値変数周辺 ($$$?$# など)
  • ((count++)) のような算術コンテキストで 、 "${arr[idx]}""${string:start:length}"
  • 内部 [[ ]] 単語の分割やグロビングの問題がない表現 (これはスタイルの問題であり、意見は大きく異なる可能性があります)
  • 単語分割が必要な場所 (for word in $words )
  • グロビングが必要な場所 (for txtfile in *.txt; do ... )
  • 必要な場所 ~ $HOME と解釈されます (~/"some dir" "~/some dir" ではありません )

こちらもご覧ください:

  • Bash の一重引用符と二重引用符の違い
  • 特別なドル記号シェル変数とは?
  • 引用とエスケープ - Bash Hackers' Wiki
  • 二重引用符が必要なのはいつですか?

Linux
  1. 二重引用符はいつ必要ですか?

  2. コマンドの出力をシェル変数に割り当てる方法は?

  3. コマンドの出力をシェル変数に保存しますか?

  1. 引用符を使用すると、なぜ単一の円記号が表示されるのですか?

  2. シェル - 変数の内容をファイルに書き込む

  3. mkdir 試行時のシェル変数の問題

  1. シェルで引用をエスケープする方法は?

  2. コマンド出力を変数に保存するときに改行を保持する方法は?

  3. 一重引用符で展開される bash 変数の問題