我具有以下目录结构,并且正在尝试创建一个简单的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...
那是为什么?
答案 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