web-dev-qa-db-ja.com

echo `date`、echo" `date`"、echo '`date`'の違いは何ですか?

これらの3つのコマンドの違いは何ですか?

echo `date`
echo "`date`"
echo '`date`'

実際の違いは何なのか混乱しています。 'が周りにある場合、それは文字列であることを意味すると思います。したがって、echoは文字列dateを日付の代わりに文字列として出力しますか?

24
John

`date`は、dateコマンドの出力に展開されます。ただし、出力に複数の連続する空白文字がある場所で余分な空白文字が削除されます。 (これは、コマンド置換がWord分割の対象であり、echoコマンドが複数の引数を処理する方法が原因です。)

"` date` "では、二重引用符は弱い引用符であるため、変数を展開し(" $ PWD "を試行)、コマンド置換を実行します。展開の結果は、single引数としてechoコマンドに渡され、連続するスペースが含まれます。つまり、ワード分割は実行されない

'`date`'では、単一引用符はより強力な引用符であるため、変数の展開やその中のコマンド置換は許可されません。

詳細は このリンク を参照してください。

下のコメント でMichael Suelmannが正しく指摘しているように、最初の点を編集しました。

どちらも

echo `date`

そして

echo "`date`"

日付が表示されます。後者の出力は、dateを単独で実行した場合の出力のようになります。

ただし、違いがあります。"の引用符"で囲まれたものは、単一の引数としてechoに送信されます。引用符は、コマンド全体の出力を1つの引数としてカプセル化します。 echoは、引数を順番に出力します。間にスペースを入れれば、基本的に同じように見えます。

ここに微妙な違いの例があります:

echo `date`

生成する:

Fri Nov 1 01:48:45 EST 2013

だが:

echo "`date`"

生成する:

Fri Nov  1 01:48:49 EST 2013

Novの後の2つのスペースは、引用符なしで1つに減らされていることに注意してください。これは、シェルがスペースで区切られた各要素を解析し、結果を6つの引数としてエコーに送信するためです。引用すると、echoは1つの引数を受け取り、引用符はスペースを保持します。

これは、echo以外のコマンドで非常に重要になります。たとえば、日付とメールアドレスの2つの引数が必要なコマンドfooを想像してみてください。

これはそのシナリオで機能します。

foo "`date`" [email protected]

しかし、これはスクリプトに7つの引数を送信して混乱させます。

foo `date` [email protected]
16
Jim Stewart

POSIXシェルでは、_`date`_は古い形式のコマンド置換です。最新の構文は$(date)です。

どちらの場合も、それらはdateの出力に展開され、末尾の改行文字が削除されます(ただし、出力にNUL文字が含まれていない場合)。

ただし、二重引用符内およびリストコンテキスト内にない場合(たとえば、echoのような単純なコマンドの引数にある場合)、その展開はさらに次の条件に従います。

  1. Word splitting:つまり、 "dateの出力で、末尾の改行文字が削除されます"は、 _$IFS_変数(デフォルトではスペース、タブ、改行(およびzshを含むNUL)の現在の値)をいくつかのwords

    たとえば、dateが_Fri 1 Nov 14:11:15 GMT 2013\n_(英語のロケールやイギリス本土のタイムゾーンでよく行うように)を出力し、_$IFS_に現在_:_が含まれている場合、次のようになります。 3つのワードに分割:_Fri 1 Nov 14_、_11_および_15 GMT 2013_。

  2. ファイル名の生成(別名globbing)(zshを除く):つまり、上記の分割では、ワイルドカード文字(_*_、_?_、_[...]_がいくつかのシェルにあります)が検索され、それらのパターンに一致するファイル名のリストに展開されます。たとえば、dateの出力が_?%? 33 */*/* UVC 3432_(金星のロケールやUVCタイムゾーンによくあるように)であり、_$IFS_がデフォルト値である場合、それはすべての中間文字が_%_、_33_である現在のディレクトリの非表示でない3文字のファイル名、現在のディレクトリのすべての非表示でないサブディレクトリのすべての非表示でないサブディレクトリにあるすべての非表示でないファイル、UVCおよび_3432_。

それが理由です:

  1. Word splittingまたはfilename generationのいずれかが必要でない限り、コマンド置換は常に(二重引用符で)引用する必要があります展開時に実行されます
  2. Word splittingが必要な場合は、_$IFS_を分割する文字に設定する必要があります。
  3. Word splittingが必要でfilename generationが不要な場合は、 _set +f_を発行して無効にします。

一重引用符はすべてを引用するため、バックティック文字は文字どおりに解釈されます。

例(_-x_を使用すると、何が起こっているかを簡単に確認できます):

_$ bash --norc -x
bash-4.2$ IFS=:
+ IFS=:
bash-4.2$ echo `date`
++ date
+ echo 'Fri  1 Nov 14' 42 '33 GMT 2013'
Fri  1 Nov 14 42 33 GMT 2013
bash-4.2$ echo "`date`"
++ date
+ echo 'Fri  1 Nov 14:42:41 GMT 2013'
Fri  1 Nov 14:42:41 GMT 2013

bash-4.2$ cd /lib/modules
+ cd /lib/modules
bash-4.2$ export TZ=UVC LC_ALL=vs_VS
+ export TZ=UVC LC_ALL=vs_VS
+ TZ=UVC
+ LC_ALL=vs_VS
bash-4.2$ unset -v IFS     # get the default behaviour
+ unset -v IFS
bash-4.2$ echo `date`
++ date
+ echo '?%?' 33 3.10-2-AMD64/build/Arch 3.10-2-AMD64/build/include 3.10-2-AMD64/build/Makefile 3.10-2-AMD64/build/Module.symvers 3.10-2-AMD64/build/scripts 3.10-2-AMD64/kernel/Arch 3.10-2-AMD64/kernel/crypto 3.10-2-AMD64/kernel/drivers 3.10-2-AMD64/kernel/fs 3.10-2-AMD64/kernel/lib 3.10-2-AMD64/kernel/mm 3.10-2-AMD64/kernel/net 3.10-2-AMD64/kernel/sound 3.10-2-AMD64/source/Arch 3.10-2-AMD64/source/include 3.10-2-AMD64/source/Makefile 3.10-2-AMD64/source/scripts 3.10-2-AMD64/updates/dkms 3.10-3-AMD64/build/Arch 3.10-3-AMD64/build/include 3.10-3-AMD64/build/Makefile 3.10-3-AMD64/build/Module.symvers 3.10-3-AMD64/build/scripts 3.10-3-AMD64/kernel/Arch 3.10-3-AMD64/kernel/crypto 3.10-3-AMD64/kernel/drivers 3.10-3-AMD64/kernel/fs 3.10-3-AMD64/kernel/lib 3.10-3-AMD64/kernel/mm 3.10-3-AMD64/kernel/net 3.10-3-AMD64/kernel/sound 3.10-3-AMD64/source/Arch 3.10-3-AMD64/source/include 3.10-3-AMD64/source/Makefile 3.10-3-AMD64/source/scripts 3.10-3-AMD64/updates/dkms UVC 3432
?%? 33 3.10-2-AMD64/build/Arch 3.10-2-AMD64/build/include 3.10-2-AMD64/build/Makefile 3.10-2-AMD64/build/Module.symvers 3.10-2-AMD64/build/scripts 3.10-2-AMD64/kernel/Arch 3.10-2-AMD64/kernel/crypto 3.10-2-AMD64/kernel/drivers 3.10-2-AMD64/kernel/fs 3.10-2-AMD64/kernel/lib 3.10-2-AMD64/kernel/mm 3.10-2-AMD64/kernel/net 3.10-2-AMD64/kernel/sound 3.10-2-AMD64/source/Arch 3.10-2-AMD64/source/include 3.10-2-AMD64/source/Makefile 3.10-2-AMD64/source/scripts 3.10-2-AMD64/updates/dkms 3.10-3-AMD64/build/Arch 3.10-3-AMD64/build/include 3.10-3-AMD64/build/Makefile 3.10-3-AMD64/build/Module.symvers 3.10-3-AMD64/build/scripts 3.10-3-AMD64/kernel/Arch 3.10-3-AMD64/kernel/crypto 3.10-3-AMD64/kernel/drivers 3.10-3-AMD64/kernel/fs 3.10-3-AMD64/kernel/lib 3.10-3-AMD64/kernel/mm 3.10-3-AMD64/kernel/net 3.10-3-AMD64/kernel/sound 3.10-3-AMD64/source/Arch 3.10-3-AMD64/source/include 3.10-3-AMD64/source/Makefile 3.10-3-AMD64/source/scripts 3.10-3-AMD64/updates/dkms UVC 3432
bash-4.2$ echo "`date`"
++ date
+ echo '?%? 33 */*/* UVC 3432'
?%? 33 */*/* UVC 3432
_

出力にNUL文字が含まれている場合、動作はシェルごとに異なります。一部は削除され、一部は出力が最初のNUL文字で切り捨てられます。zshはそれらを保持しますが、とにかく外部コマンドはNULを含む引数を取ることができません。

3

単語の分割はコマンドの置換後に行われるため、 `date`を使用すると、日付の出力が複数の単語に分割されます。

「 `date`」を使用すると、二重引用符の間にコマンド置換があるため、日付の出力を1つのワード/パラメーターとして取得しますが、出力はそれ以上解析されません。以下の私の例の「$ i」のような変数展開でも同じことが当てはまります。

'`date`'を使用すると、単一引用符の間にコマンド置換がないため、リテラル` date`を取得します。

おそらく、3つの形式の違いは、このようにしてより明確になります。

> for i in `date`; do echo "$i"; done
Fr
1.
Nov
12:25:30
CET
2013

> for i in "`date`"; do echo "$i"; done
Fr 1. Nov 12:25:38 CET 2013

> for i in '`date`'; do echo "$i"; done
`date`
0