在 OS X 10.11.1 上运行时,我在控制台中逐一执行以下一系列命令:
FILE="a b c.tiff" # file in the current folder
VAR=$(mdls -name kMDItemContentCreationDate $FILE) # storing the creation time string
TS=$(echo ${VAR[2]}; echo ${VAR[3]} # saving the date and time only
echo $TS
扩展工作得很好。输出显示:
2016-01-16 15:34:29
但是,当我将这些保存在脚本中并运行它时,评估过程中似乎出现了一些不同。
调试(使用重击)得出:
FILE='a b c.tiff'
mdls -name kMDItemContentCreationDate a b c.tiff
VAR='a: could not find a.'
echo
echo
TS=
echo
,因此我看到扩张的行为有所不同。
我担心的是为什么会出现这种差异以及我应该如何纠正我的脚本。谢谢。
答案1
我敢肯定您没有在控制台中运行过这些命令,否则您会得到相同的结果。这里有两个严重的问题,以及一些糟糕的脚本编写实践。首先是严重的问题:
当您引用一个变量(例如
$FILE
)而没有用双引号括起来时,shell 会将其拆分为“单词”,然后展开所有通配符,然后再将其传递给命令。在本例中,这意味着 将a b c.tiff
被拆分为“a”、“b”和“c.tiff”。这就是您收到“a:无法找到 a。”错误的原因。解决方案:除非您特别想要分词和通配符扩展,否则应将变量引用放在双引号中。(有些情况下,省略双引号是安全的,但跟踪它们会比它的价值更麻烦。只要养成使用双引号的习惯即可。)
当你使用像这样的赋值时
VAR=$(somecommand)
,它会将变量赋值为纯字符串,不是一个数组。要将其存储为数组,请在右侧使用括号,如VAR=( $(somecommand) )
。请注意,由于$(somecommand)
不在双引号中,因此它将被拆分成单词并进行通配符扩展,但在这种情况下,我们希望拆分单词(因此每个“单词”都存储在单独的数组元素中),并且输出格式足够可预测,通配符扩展不会做任何奇怪的事情来搞砸我们。所以这是少数可以省略双引号的情况之一。
修复这两个问题后,第二行变成:
VAR=( $(mdls -name kMDItemContentCreationDate "$FILE") )
现在,有些不良的脚本习惯实际上并没有造成麻烦:
在赋值中
TS=$(echo ${VAR[2]}; echo ${VAR[3]})
(注意:我添加了缺失的右括号),命令替换和echo
命令没有做任何有用的echo
事情。它所做的是获取数组元素值,对它们进行单词拆分和通配符扩展(这里什么都不做),将它们作为参数传递给命令,获取这些命令的输出并将其收集到变量中。将两个字符串粘在一起需要做很多工作。只需使用`TS="${VAR[2]} ${VAR[3]}"。顺便说一句,它还做了一些有点奇怪的事情:它将字符串粘在一起,并在它们之间添加换行符。当您使用 打印它时
echo $TS
,它(再次)被拆分成单词,因此每行都被视为 的单独参数echo
,这会在参数之间添加空格。最终结果:该echo
命令实际上将换行符转换为空格。首先将其设置为空格,然后在使用它时将其双引号括起来会更简洁,这样您的脚本就不会依赖于两个恰好相互抵消的奇怪行为。最后,使用全大写变量并不安全。有许多全大写变量对 shell(和某些命令)具有特殊含义,如果您不小心使用其中一个,可能会得到奇怪的结果。典型的例子是将某个变量分配给 PATH,此时所有命令突然变得无法识别。很难跟踪所有魔法变量,因此只需使用小写变量即可,这样就安全了。
清理完所有这些后,我得到的脚本内容如下:
file="a b c.tiff" # file in the current folder
var=( $(mdls -name kMDItemContentCreationDate "$file") ) # storing the creation time string as an array
ts="${var[2]} ${var[3]}" # saving the date and time only
echo "$ts"
最后说明:如有疑问,请运行脚本shellcheck.net——它会指出许多标准的初学者错误并为您节省大量时间!