247

シェルスクリプトで変数を引用符で囲む必要がありますか、またはしないでください。

たとえば、次は正しいですか。

xdg-open $URL
[ $? -eq 2 ]

また

xdg-open "$URL"
[ "$?" -eq "2" ]

もしそうなら、なぜですか?

4

6 に答える 6

200

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

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

文字列の方が安全なので、習慣から外して文字列を引用する傾向があります。

于 2012-04-08T23:08:37.420 に答える
137

要するに、単語分割とワイルドカード拡張を実行するためにシェルを必要としないすべてを引用してください。

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

$ 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には、やなどのシェルメタ文字が含まれているため&)。通常、正規表現を含むものはすべて引用符で囲む必要があります(同上同上)。非空白文字の間に単一のスペース以外の重要な空白を含むものはすべて引用符で囲む必要があります(そうでない場合、シェルは空白を事実上単一のスペースに変換し、先頭または末尾の空白をトリミングします)。

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

変数ではない値は基本的に同じ規則に従いますが、メタ文字を引用符で囲む代わりにエスケープすることもできます。&一般的な例では、メタ文字がエスケープまたは引用符で囲まれていない限り、その中にaが含まれる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。ただし、ワイルドカードの一致をループすることは一般的な問題であり、頻繁に誤って実行されます。)

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

于 2014-12-30T07:58:12.860 に答える
39

一般的な見積もりの​​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"

参照:

于 2017-02-08T04:18:46.730 に答える
5

スペースが含まれていない"$var"ことが確実でない限り、私は通常、安全のために引用符を使用します。$var

私は$var線を結合する簡単な方法として使用します:

lines="`cat multi-lines-text-file.txt`"
echo "$lines"                             ## multiple lines
echo $lines                               ## all spaces (including newlines) are zapped
于 2018-01-16T17:14:26.353 に答える
0

エディターのhttps://www.shellcheck.net/プラグインが指示するときはいつでも。

于 2022-02-02T15:34:56.753 に答える
-2

シェルスクリプトで変数を使用するには、引用符で囲まれた変数を使用します。引用符で囲まれた変数は、シェルスクリプトの実行に影響を与えないスペースまたは特殊文字が変数に含まれている可能性があることを意味します。それ以外の場合、変数名にスペースや特殊文字が含まれていないことが確実な場合は、「」なしで使用できます。

例:

echo "$url name"  # (Can be used at all times)

echo "$url name"  # (Cannot be used at such situations so take precaution before using it)
于 2018-09-21T03:47:09.113 に答える