这三个命令有什么区别呢?
echo `date`
echo "`date`"
echo '`date`'
我对实际上的差异是什么感到困惑。我认为当 ' 周围时意味着它是一个字符串,因此 echo 会字面地输出字符串date
而不是显示日期?
答案1
答案2
两个都
echo `date`
和
echo "`date`"
将显示日期。后者的输出看起来像是date
单独运行的输出。
不过,还是有区别的:用"
引号括起来的参数将作为单个参数"
发送到 to 。echo
引号将整个命令的输出封装为一个参数。由于echo
只是按顺序打印出其参数,中间有空格,所以它看起来基本上是一样的。
这是一个细微差别的例子:
echo `date`
产生:
Fri Nov 1 01:48:45 EST 2013
但:
echo "`date`"
产生:
Fri Nov 1 01:48:49 EST 2013
请注意,后面的两个空格Nov
已减少为一个(不带引号)。这是因为 shell 正在解析每个空格分隔的元素并将结果作为 6 个参数发送到 echo。当您引用它时,echo 接收一个参数,并且引号保留空格。
这在除 echo 之外的命令中变得更加重要。例如,假设一个命令foo
需要两个参数:日期和电子邮件地址。
这将在这种情况下起作用:
foo "`date`" [email protected]
但这会向脚本发送 7 个参数,从而使脚本感到困惑:
foo `date` [email protected]
答案3
在 POSIX shell 中,`date`
是命令替换的古老形式。现代语法是$(date)
.
在这两种情况下,它们都会扩展为 的输出,date
并删除尾随换行符(前提是输出不包含 NUL 字符)。
但是,当不在双引号内和列表上下文中时(例如在简单命令的参数中,如echo
您的情况),该扩展进一步受以下约束:
分词: 那就是“输出
date
删除了尾随换行符”根据$IFS
变量的当前值(默认包含空格、制表符和换行符(以及 NUL 和zsh
))拆分为几个字。例如,如果
date
输出Fri 1 Nov 14:11:15 GMT 2013\n
(就像在英语语言环境和英国大陆时区中经常做的那样),并且$IFS
当前包含:
,那么它将被分成 3 个字:Fri 1 Nov 14
,11
和15 GMT 2013
。文件名生成(又名通配)(除了
zsh
):也就是说,上面分割产生的每个单词都会查找通配符(*
,?
,[...]
尽管某些 shell 有更多),并扩展到与这些模式匹配的文件名列表。例如,如果 的输出date
是?%? 33 */*/* UVC 3432
(就像在金星语言环境和 UVC 时区中经常出现的那样),并且$IFS
是默认值),那么它将扩展到当前目录中中间字符为%
,33
,的所有非隐藏 3 字符文件名。当前目录所有非隐藏子目录的所有非隐藏子目录中的所有非隐藏文件,UVC
以及3432
.
因此:
- 您应该始终引用(用双引号)命令替换,除非您确实想要分词或者文件名生成在其扩展时执行
- 如果你确实想要分词,那么你应该设置
$IFS
为你想要分割的字符。 - 除了 zsh 之外,如果你愿意的话分词但不是 文件名生成,您需要发出
set -o noglob
(又名set -f
)来禁用它。
单引号引用所有内容,因此会导致反引号字符按字面意思理解。
示例(使用-o xtrace
可以更容易地了解发生了什么):
$ bash --norc -o xtrace
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 字符,则不同 shell 的行为会有所不同:有些会删除它们,有些会在第一个 NUL 字符处截断输出,zsh
保留它们,但请注意,无论如何外部命令都不能接受包含 NUL 的参数
答案4
使用“date”,您可以将日期的输出拆分为多个单词,因为单词拆分是在命令替换后完成的。
使用“`date`”,您可以将日期输出作为一个单词/参数,因为双引号之间存在命令替换,但输出不会进一步解析。对于像下面的示例中的“$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`