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 通常会通过参数列表传递通配符。
我想要运行这个命令行脚本
$ 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 通常会通过参数列表传递通配符。