Bash glob 参数只显示第一个文件而不是所有文件

Bash glob parameter only shows first file instead of all files

我想要运行这个命令行脚本

$ script.sh   lib/* ../test_git_thing

我希望它处理 /lib 文件夹中的所有文件。

FILES=
for f in $FILES
do
  echo "Processing $f file..."
done

目前只打印第一个文件。如果我使用 $@,它会给我所有的文件,还有我不想要的最后一个参数。有什么想法吗?

当您调用 "script.sh lib/*" 时,参数列表将在命令行中扩展,您的脚本将以 lib/ 中的所有文件作为参数被调用。由于您只在脚本中引用 $1,因此它只打印第一个文件。您需要在命令行中对通配符进行转义,以便将其传递给您的脚本以执行 globbing。

正如正确指出的那样,命令行上的 lib/* 正在扩展到 lib 中的所有文件。为防止扩展,您有 2 个选择。 (1) 引用您的输入:

$ script.sh 'lib/*' ../test_git_thing

或者 (2),关闭文件通配。但是,选项 set -f 将禁用 shell 中的路径名扩展,但它会禁用所有路径名扩展(在脚本中设置它没有帮助,因为扩展是由 shell 之前完成的将参数传递给您的脚本)。在您的情况下,最好引用输入或将第一个参数作为目录名传递,并在脚本中添加扩展:

DIR=
for f in "$DIR"/*

在 bash 和 ksh 中,您可以遍历除最后一个参数之外的所有参数,如下所示:

for f in "${@:1:$#-1}"; do
  echo "$f"
done

在 zsh 中,你可以做类似的事情:

for f in $@[1,${#}-1]; do
  echo "$f"
done

$# 是参数个数,${@:start:length} 是 bash 和 ksh 中的 substring/subsequence 符号,而 $@[start,end] 是 zsh 中的子序列。在所有情况下,下标表达式都被计算为算术表达式,这就是 $#-1 有效的原因。 (在 zsh 中,你需要 ${#}-1 因为 $#- 被解释为 "the length of $-"。)

在所有三个 shell 中,您可以使用带有标量变量的 ${x:start:length} 语法来提取子字符串;在 bash 和 ksh 中,您可以将 ${a[@]:start:length} 与数组一起使用以提取值的子序列。

这回答了给定的问题,没有使用非 POSIX 功能,也没有解决方法,例如禁用 globbing。

您可以使用循环找到最后一个参数,然后在处理文件列表时排除它。在这个例子中,$d 是目录名,而 $f 与原始答案中的含义相同:

#!/bin/sh
if [ $# != 0 ]
then
        for d in "$@"; do :; done
        if [ -d "$d" ]
        then
                for f in "$@"
                do
                        if [ "x$f" != "x$d" ]
                        then
                                echo "Processing $f file..."
                        fi
                done
        fi
fi

此外,最好也测试 "$f" 是否是一个文件,因为如果找不到匹配项,shell 通常会通过参数列表传递通配符。