在脚本的命令行上传递glob时,只会扩展第一项

时间:2019-02-11 01:06:29

标签: bash shell

我具有以下目录结构,并且正在尝试创建一个简单的bash脚本,以打印出所有带有foo * / scripts / * A.txt模式(即foo1 / scripts / fileA.txt和foo2 / scripts / fileA)的文件。 txt)

$ tree

.
├── bar
│   └── scripts
│       ├── fileA.txt
│       └── fileB.txt
├── bash_scrap.sh
├── foo1
│   └── scripts
│       ├── fileA.txt
│       └── fileB.txt
└── foo2
    └── scripts
        ├── fileA.txt

    └── fileB.txt

我现在创建一个脚本bash_scrap.sh,在其中我要以第一个命令行参数给出的模式打印所有文件名。

$ cat bash_scrap.sh 
#!/bin/bash
FILES=$1 
for f in $FILES
do
  echo "Processing $f file..."
  printf "\n\n"
done

当我直接定义文件并在终端中运行脚本时,我得到了预期的输出

$ FILES=foo*/scripts/*A.txt
$ for f in $FILES; do   echo "Processing $f file...";   printf "\n\n"; done
Processing foo1/scripts/fileA.txt file...


Processing foo2/scripts/fileA.txt file...

但是,如果我尝试将其作为具有输入模式的脚本来运行,它只会打印出第一个文件名。

$ ./bash_scrap.sh foo*/scripts/*A.txt
Processing foo1/scripts/fileA.txt file...

那是为什么?

2 个答案:

答案 0 :(得分:5)

$ ./bash_scrap.sh foo*/scripts/*A.txt

...不将foo*/scripts/*A.txt放在$1中。相反,它会扩展 foo*/scripts/*A.txt,并将第一个结果放入$1,将第二个结果放入$2,依此类推。这是在脚本启动之前发生的(并且是由调用外壳执行的,而不是由运行脚本的外壳执行的),因此您无法通过修改脚本的文本来避免这种情况。

如果希望将glob作为文字值传递给脚本并仅在该脚本启动后才对其进行解释,则需要将其引号:

$ ./bash_scrap.sh 'foo*/scripts/*A.txt'

但是,一种更好的方法是期望父shell为您完成该扩展,并遍历"$@"

for f do
  echo "Processing $f file"
done

这是ls之类的程序的工作方式:运行ls *.txt时,用户的外壳程序*.txt替换为匹配的文件列表 ls甚至开始了ls不会出现问题,如果您给它一个,它也不会正常工作(尝试运行ls '*.txt',您会发现它总是会给找不到文件的错误,除非您使用该文字名称创建了文件)。

答案 1 :(得分:0)

按如下所示更新脚本应该可以解决此问题。注意$ @而不是其他答案指出的$ 1。 需要从$ @中忽略$ 0,这就是使用IGNORE_FIRST_ARG变量的原因。

#!/bin/bash
IGNORE_FIRST_ARG=1
for f in "$@"
do
    if [ $IGNORE_FIRST_ARG == 1 ]
    then
        IGNORE_FIRST_ARG=0
        continue
    fi  
    echo "Processing $f file..."
    printf "\n\n"
done