带引号“ $()”的变量


12

我写了这个脚本:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

我是一个尝试快速学习的新手,但对变量的引号有疑问。

在$()之间放置一个变量,我明白了,但是为什么即使在语句中也需要引号if呢?是否要执行嵌套命令?


5
请注意,这while [ true ]确实会产生无限循环,但可能并非出于您认为确实如此的原因。while [ false ] 还会产生一个无限循环,因为只有一个参数,[ ... ]如果该参数是非空字符串,则成功。while true实际上会运行一个名为true(总是成功的)命令。
chepner

4
您不要在其中放入变量$()。你把一个命令$()
通配符

Answers:


23

引号可以防止“单词分裂”。也就是说:将变量分解为多个空格字符(或更准确地说,是在默认$IFSshell变量的值中定义的空格,制表符和换行符)。

例如,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

在这里,我们定义了一个howmany函数,该函数仅让我们知道给出了多少个位置参数。如您所见,有两个项目传递给该变量,并且用引号将变量中的文本视为一个单位。

这对于准确传递信息很重要。例如,如果变量包含文件的路径,而文件名包含路径中任何位置的空格,则您尝试运行的命令可能会失败或给出不正确的结果。如果我们尝试使用$var变量touch $var创建文件,则将创建两个文件,但touch "$var"只有一个。

您也一样[ "$currentoutput" != "$lastoutput" ]。此特定测试对两个字符串进行比较。运行测试时,该[命令将需要查看3个参数-一个文本字符串,!=运算符和另一个文本字符串。保留双引号可以防止单词分裂,并且该[命令可以准确地看到这三个参数。现在,如果变量不加引号怎么办?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

在这里,发生单词拆分,而是[看到两个字符串helloworld然后是!=,之后是两个其他字符串hi world。关键是没有双引号,变量的内容应理解为单独的单位,而不是一个整体。

分配命令替换不需要双引号,如

var=$( df )

df命令输出保存到的位置var。但是,$(...)除非您确实希望将输出视为单独的项目,否则始终将引号和命令替换双引号是一个好习惯。


附带一提,

while [ true ]

部分可以是

while true

[是一个评估其参数的命令,[ whatever ]无论内部内容如何,该命令始终为true。相比之下,while true使用true总是返回成功退出状态的命令(而这正是while循环所需要的)。区别是更加清晰,执行的测试更少。或者,您也可以使用:代替true

echo "" date and Time部分双引号可能会被删除。它们仅插入一个空字符串,并在输出中添加额外的空间。如果需要的话,请随时将它们保留在此处,但是在这种情况下没有特殊的功能价值。

lsusb >> test.log

这部分可能用代替echo "$currentoutput" >> test.loglsusb已经在中运行后,没有理由再次运行currentoutput=$(lsusb)。如果 必须在输出中保留结尾的换行符,则可以多次运行命令来看到该值,但是在lsusb不需要时可以看到该值。调用的外部命令越少越好,因为每次调用非内置命令都会增加CPU,内存使用和执行时间的成本(即使这些命令可能是从内存中预先加载的)。


也可以看看:


1
很好的答案,如果您还没有的话,应该写一本有关的书bash。但是,最后一部分应该echo "$currentoutput" >> test.log 更好 printf '%s\n' "$currentoutput" >> test.log吗?
pLumo

2
@RoVo是的,printf应该优先考虑可移植性。由于此处使用的是bash特定的脚本,因此可以原谅使用echo。但是您的观察非常正确
Sergiy Kolodyazhnyy

1
@SergiyKolodyazhnyy,......我不知道这echo是即使在外壳被称为是bash的(和的价值完全可靠xpg_echoposix被称为标志); 如果您的值可以是-n-e,它可以消失而不是被打印。
查尔斯·达菲,

4

currentoutput="$(lsusb)"lsusb中不是变量,它是命令。该语句的作用是执行lsusb命令并将其输出分配给currentoutput变量。

较旧的语法是

currentoutput=`lsusb`

您可以在许多示例和脚本中找到它

要回答您问题的另一部分,if [ ]就是ifbash中语法的定义方式。在https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html中查看更多信息


3
我认为重要的是说这[ ]实际上是test命令。您还可以将if语句与其他命令一起使用,因为它依赖于其退出代码的0或非零测试。
Arronical

...实际上,运行if grep -qe "somestring" file要比运行好得多grep -qe "somestring" file; if [ $? = 0 ]; then ...,因此,if [ ...作为if语法定义一部分的要求不仅引起误解,而且还会导致不良做法。
查尔斯·达菲

4

下面的命令运行外部命令command并返回其输出。

"$(command)"

如果没有括号/括号,它将查找变量而不是运行命令:

"$variable"

至于$variable和之间的区别"$variable",当$variable包含空格时,这一点变得重要。使用时"$variable",即使变量内容中包含空格,整个变量内容也会插入到单个字符串中。使用$variable变量的内容时,可能会扩展为多个参数的参数列表。


嗨@thomasrutter,对不起,我的意思是引号...我现在编辑我的评论!
Shankhara

1

言归正传-bash建议您使用[[ ... ]]over [ ... ]结构避免在测试中引用变量,从而避免其他人指出的分词问题。

[在bash中提供以实现POSIX的兼容性,以便与要在bash下运行的脚本#!/bin/sh或移植到bash的脚本兼容-在大多数情况下,应避免使用[[

例如

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal

bash没有提出这样的建议;它提供了 [[ ... ]]可能更方便的功能,并且提供了一些不方便的功能[ ... ],但是[ ... ] 正确使用并没有问题。
chepner

@chepner-也许我应该在这里更清楚一些。当然,这不是bash手册中的明确建议,但是考虑到[[一切都可以[做,而且还有很多次仍然使[人们绊倒,然后尝试[[重新设计回溯功能,以[使失败并散布错误-可以说这是一个合理的建议。就最相关的bash风格指南而言,社区推荐不要使用[
shalomb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.