Bash 在脚本上传递glob';s命令行,仅展开第一项

Bash 在脚本上传递glob';s命令行,仅展开第一项,bash,shell,Bash,Shell,我有下面的目录结构,正在尝试创建一个简单的bash脚本,以打印出模式为foo*/scripts/*a.txt(即foo1/scripts/fileA.txt和foo2/scripts/fileA.txt)的所有文件 现在,我创建了一个脚本bash_scrap.sh,希望在其中打印所有文件名,模式由第一个命令行参数给定 $ cat bash_scrap.sh #!/bin/bash FILES=$1 for f in $FILES do echo "Processing $f file..

我有下面的目录结构,正在尝试创建一个简单的bash脚本,以打印出模式为foo*/scripts/*a.txt(即foo1/scripts/fileA.txt和foo2/scripts/fileA.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...
为什么呢

$ ./bash_scrap.sh foo*/scripts/*A.txt
…不会将
foo*/scripts/*A.txt
放入
$1
中。相反,它展开
foo*/scripts/*A.txt
,并将第一个结果放入
$1
,第二个结果放入
$2
,等等。这发生在脚本启动之前(由调用shell执行,而不是由运行脚本的shell执行),因此您无法通过修改脚本文本来避免它

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

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

但是,一种更好的方法是期望父shell为您进行扩展,并迭代
“$@”

这就是像
ls
这样的程序的工作原理:当您运行
ls*.txt
时,用户的shell会在启动
ls
之前用匹配文件列表替换
*.txt
ls
不希望得到一个glob,如果你给它一个glob,它就不能正常工作(试着运行
ls'*.txt'
,你会看到它总是给出一个找不到文件的错误,除非你创建了一个具有该文本名称的文件)

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

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

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

但是,一种更好的方法是期望父shell为您进行扩展,并迭代
“$@”


这就是像
ls
这样的程序的工作原理:当您运行
ls*.txt
时,用户的shell会在启动
ls
之前用匹配文件列表替换
*.txt
ls
不希望被赋予glob,如果你给它一个glob,它就不能正常工作(试着运行
ls'*.txt'
,你会看到它总是给出一个找不到文件的错误,除非你创建了一个具有该文本名称的文件)。

按照下面的方式更新脚本应该可以解决这个问题。请注意$@而不是其他答案所指出的$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

按以下方式更新脚本应该可以解决此问题。请注意$@而不是其他答案所指出的$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

请不要使用
文件=$@
。这会将所有参数混合在一起,它们之间只有空格,但由于空格可以出现在参数(和文件名)中,因此您现在无法区分哪些空格是独立的参数,哪些只是参数的一部分。相反,可以跳过变量,直接在“$@”中为f使用
(或者用缩写
表示f do
),或者将arg列表存储为一个数组:
files=(“@”)
(注意括号——这将创建一个数组,并将每个arg存储为一个单独的元素),然后像在“${files[@]中为f使用
一样使用它
。请不要使用
文件=$@
。这会将所有参数混合在一起,它们之间只有空格,但由于空格可以出现在参数(和文件名)中,因此您现在无法区分哪些空格是独立的参数,哪些只是参数的一部分。相反,可以跳过变量,直接在“$@”
中为f使用
(或者用缩写
表示f do
),或者将arg列表存储为数组:
files=(“@”)
(注意括号——这将创建一个数组,并将每个arg存储为单独的元素),然后像在“${files[@]}”中为f使用
一样使用它。