脚本不是全局扩展的,但在将罪魁祸首作为简约示例运行时可以正常工作

时间:2015-05-02 22:44:04

标签: bash

我已经在这个问题上花了好几个小时,但不能直接说出来。

这个最小脚本

#!/bin/bash
wipe_thumbs=1

if (( wipe_thumbs )); then
 src_dir=$1
 thumbs="$src_dir"/*/t1*.jpg
 echo $thumbs
fi

使用./script workdir进行调用,并显示workdir的所有子目录中以t1 *开头的大量文件名。

将上述if-case放在较大的脚本中时,不执行globbing:

SRC: -- workdir/ --
THUMBS: -- workdir//*/t1*.jpg --
ls: cannot access workdir//*/t1*.jpg: No such file or directory

与大脚本和最小脚本的唯一区别是大脚本有路径验证器和getopts-extractor。此代码紧跟if-case:

#!/bin/bash
OPTIONS=":ts:d:"
src_dir=""
dest_dir=""
wipe_thumbs=0

while getopts $OPTIONS opt ; do
  case "$opt" in
    t) wipe_thumbs=1
       ;;
  esac
done
shift $((OPTIND - 1))

src_dir="$1"
dest_dir="${2:-${src_dir%/*}.WORK}"

# Validate source
echo -n "Validating source..."
if [[ -z "$src_dir" ]]; then
  echo "Can't do anything without a source-dir."
  exit
else
  if [[ ! -d "$src_dir" ]]; then
    echo "\"$src_dir\" is really not a directory."
    exit
  fi
fi
echo "done"

# Validate dest
echo -n "Validating destination..."
if [[ ! -d "$dest_dir" ]]; then
  mkdir "$dest_dir"
  (( $? > 0 )) && exit
else
  if [[ ! -w "$dest_dir" ]]; then
    echo "Can't write into the specified destination-dir."
    exit
  fi
fi
echo "done"


# Move out the files into extension-named directories
echo -n "Moving files..."

if (( wipe_thumbs )); then
  thumbs="$src_dir"/*/t1*.jpg               # not expanded
  echo DEBUG THUMBS: -- "$thumbs" --
  n_thumbs=$(ls "$thumbs" | wc -l)
  rm "$thumbs"
fi

...rest of script, never reached due to error...

有人可以对此有所了解吗?为什么不在大脚本中扩展glob,但在简约测试脚本中工作正常?

编辑:添加了完整的if-case。

1 个答案:

答案 0 :(得分:4)

问题在于,通配符语句中没有扩展通配符(例如thumbs="$src_dir"/*/t1*.jpg),但是在没有双引号的情况下使用变量时会扩展通配符。这是一个互动的例子:

$ src_dir=workdir
$ thumbs="$src_dir"/*/t1*.jpg
$ echo $thumbs    # No double-quotes, wildcards will be expanded
workdir/sub1/t1-1.jpg workdir/sub1/t1-2.jpg workdir/sub2/t1-1.jpg workdir/sub2/t1-2.jpg
$ echo "$thumbs"    # Double-quotes, wildcards printed literally
workdir/*/t1*.jpg
$ ls $thumbs    # No double-quotes, wildcards will be expanded
workdir/sub1/t1-1.jpg   workdir/sub2/t1-1.jpg
workdir/sub1/t1-2.jpg   workdir/sub2/t1-2.jpg
$ ls "$thumbs"    # Double-quotes, wildcards treated as literal parts of filename
ls: workdir/*/t1*.jpg: No such file or directory

...所以快速简单的解决方法是从lsrm命令中删除双引号。但这不安全,因为如果$src_dir包含任何空格或通配符,它​​也会导致解析问题(这对你来说可能不是问题,但我习惯于OS X,其中文件名中的空格是无处不在,我已经学会了对这些事情要小心)。最好的方法是将拇指文件列表存储为数组:

$ src="work dir"
$ thumbs=("$src_dir"/*/t1*.jpg)    # No double-quotes protect $src_dir, but not the wildcard portions
$ echo "${thumbs[@]}"    # The "${array[@]}" idiom expands each array element as a separate word
work dir/sub1/t1-1.jpg work dir/sub1/t1-2.jpg work dir/sub2/t1-1.jpg work dir/sub2/t1-2.jpg
$ ls "${thumbs[@]}"
work dir/sub1/t1-1.jpg  work dir/sub2/t1-1.jpg
work dir/sub1/t1-2.jpg  work dir/sub2/t1-2.jpg

如果没有任何匹配项,您可能还需要设置nullglob(因此它将扩展为零长度数组)。

在你的剧本中,这就是这样的:

if (( wipe_thumbs )); then
  shopt -s nullglob
  thumbs=("$src_dir"/*/t1*.jpg)              # expanded as array elements
  shopt -u nullglob    # back to "normal" to avoid unexpected behavior later
  printf 'DEBUG THUMBS: --'
  printf ' "%s"' "${thumbs[@]}"
  printf ' --\n'
  # n_thumbs=$(ls "${thumbs[@]}" | wc -l)    # wrong way to do this...
  n_thumbs=${#thumbs[@]}    # better...
  if (( n_thumbs == 0 )); then
    echo "No thumb files found" >&2
    exit
  fi
  rm "${thumbs[@]}"
fi