在脚本的命令行上传递glob,只展开第一个项目

big*_*377 0 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
Run Code Online (Sandbox Code Playgroud)

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

$ cat bash_scrap.sh 
#!/bin/bash
FILES=$1 
for f in $FILES
do
  echo "Processing $f file..."
  printf "\n\n"
done
Run Code Online (Sandbox Code Playgroud)

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

$ 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...
Run Code Online (Sandbox Code Playgroud)

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

$ ./bash_scrap.sh foo*/scripts/*A.txt
Processing foo1/scripts/fileA.txt file...
Run Code Online (Sandbox Code Playgroud)

这是为什么?

Cha*_*ffy 5

$ ./bash_scrap.sh foo*/scripts/*A.txt
Run Code Online (Sandbox Code Playgroud)

......不把foo*/scripts/*A.txt$1.相反,它扩展 foo*/scripts/*A.txt,并将第一个结果放入$1,第二个结果$2,等等.这发生在脚本启动之前(由调用shell执行,而不是运行脚本),所以你无法避免它通过修改脚本的文本.

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

$ ./bash_scrap.sh 'foo*/scripts/*A.txt'
Run Code Online (Sandbox Code Playgroud)

但是,更好的方法是期望父shell为您进行扩展,并迭代"$@":

for f do
  echo "Processing $f file"
done
Run Code Online (Sandbox Code Playgroud)

这就是程序之类的ls工作方式:运行时ls *.txt,用户的shell会启动之前替换*.txt为匹配文件列表.预计不会给予水珠,如果您无法正常工作给它一个(尝试运行,你会看到它总是给一个文件未找到错误,除非你已经创建了一个文件与字面名称).lslsls '*.txt'