【问题标题】:How do I use a wildcard with a getopts in a bash script?如何在 bash 脚本中使用带有 getopts 的通配符?
【发布时间】:2024-01-08 17:09:01
【问题描述】:

我有一个 bash 脚本,我想接受一些可选的输入参数,后跟一个文件名或包含通配符的文件规范。

如果我跑 getopts_problem.sh -td *.txt 该脚本愉快地输出当前目录中的 .txt 文件列表:

文件是 readme.txt letter_to_editor.txt someotherfile.txt

如果我运行getopts_problem.sh -td *.ABC 脚本输出

文件是 *.ABC

当前目录中没有扩展名为“.ABC”的文件。然后,出于某种原因,脚本将“*.ABC”解释为文件名。如何让脚本将“*.ABC”识别为要扩展的文件名表达式,而不是实际的文件名?代码如下:

# !/bin/sh
doDry=0
doTimestamp=0

while getopts ":dt" OPT;
do
   case $OPT in
      d ) doDry=1 ;;
      t ) doTimestamp=1 ;;
      ? ) echo 'Bad options used. '
          exit 1 ;;
    esac
done

shift $(($OPTIND - 1))
fileList=$@

for file in "$fileList"
do
  echo file is $file
done

exit 0

【问题讨论】:

标签: bash getopts


【解决方案1】:

通配符模式在执行脚本之前由bash 扩展。不是解释脚本的外壳,而是您运行它的外壳。它是否保持模式、不通过或失败取决于nullglobfailglob 和一些此类选项(请参阅man bash)。

另外,for file in "$fileList" 引号明确告诉 shell 扩展变量。

【讨论】:

  • 语义狡辩:双引号允许变量扩展(即用其值替换其名称),但防止分词(即基于空格将其分成多个词)。最终结果:循环只运行一次,$file 设置为整个文件列表。解决方案是使用数组:fileList=("$@"); for file in "${fileList[@]}"; ...——这会正确破坏文件列表,即使某些文件名包含空格或其他“有趣”字符。
  • 您对 $fileList 周围的引号是正确的。我把它们拿出来,现在有三行“file is”输出,分别列出了每个 .txt 文件。但是它仍然认为“*.ABC”是一个文件名,这是一个问题。我想出的解决方法是在代码主体中使用[ -e $file ] 来测试文件是否存在。我不知道这是否是公认的方式,或者它是否是一个巨大的组合,但它确实有效。
  • @Kevin,就像我说的,不匹配文件的通配符的扩展由 nullglob 控制,请在 bash 手册页中查找。还要记住,除非你引用它,否则它将由你用来启动脚本的 shell 处理。请记住这一点。
  • 但是您还说扩展发生在运行脚本的外壳中。这是否意味着如果我要使用 nullglob 来控制扩展,我必须在运行脚本之前在命令行上设置 nullglob?
  • 是的,通常可执行文件会验证通过的文件,无论其来源如何。我了解您不希望用户设置 nullglob,我并不是说提出这样的要求真的很酷,我只是在解释事物的本质;)