为什么 [ "$(ls *.jpg | wc -l)" = 0 ] 总是假的,除非去掉引号?

aaf*_*lei 2 bash shell

由于没有jpg在我的当前工作目录下的文件,为什么两个脚本jpg1.sh,并jpg2.sh给出不同的结果?如何理解差异?请注意,脚本之间的唯一区别是是否在$()命令替换周围使用双引号。

$ cat jpg1.sh
#!/bin/bash
if [ "$(ls *.jpg | wc -l)" = 0 ]; then
    echo "yes"
else
    echo "no"
fi

$ ./jpg1.sh
ls: *.jpg: No such file or directory
no

$ cat jpg2.sh
#!/bin/bash
if [ $(ls *.jpg | wc -l) = 0 ]; then
    echo "yes"
else
    echo "no"
fi

$ ./jpg2.sh
ls: *.jpg: No such file or directory
yes
Run Code Online (Sandbox Code Playgroud)

跟进

我证明打勾的答案是关键——wc -l在其返回值中有一些额外的空格。添加set -x到两个脚本后,差异就会显现出来。

$ ./jpg1.sh
++ wc -l
++ ls '*.jpg'
ls: *.jpg: No such file or directory
+ '[' '       0' = 0 ']'
+ echo no
no

$ ./jpg2.sh
++ wc -l
++ ls '*.jpg'
ls: *.jpg: No such file or directory
+ '[' 0 = 0 ']'
+ echo yes
yes
Run Code Online (Sandbox Code Playgroud)

顺便说一下,我的系统是 macOS Catalina。

Cha*_*ffy 8

在某些系统上,这是错误的,因为wc -l用空格填充其答案,而您要比较的字符串 -- 0-- 根本不包含任何空格。使用引号时,会比较准确的输出;当您不使用它们时,输出会根据空格拆分为单独的单词,并且在将这些单词放入[命令行之前,这些单词中的每一个都被扩展为一个 glob 。要确定您是否在这样的系统上,请添加set -x到您的脚本以启用跟踪级别日志记录,以便您可以查看[要求比较的确切值。

在目前的情况下,整个问题是平凡避免:没有理由使用两种 lswc用于此目的。

#!/usr/bin/env bash

shopt -s nullglob
jpegs=( *.jpg )
if (( ${#jpegs[@]} == 0 )); then
  echo "No JPEG files were found"
else
  echo "Exactly ${#jpegs[@]} JPEG files were found"
fi
Run Code Online (Sandbox Code Playgroud)