Bash glob 参数只显示第一个文件而不是所有文件
Posted
技术标签:
【中文标题】Bash glob 参数只显示第一个文件而不是所有文件【英文标题】:Bash glob parameter only shows first file instead of all files 【发布时间】:2015-06-09 17:12:55 【问题描述】:我想运行这个命令行脚本
$ script.sh lib/* ../test_git_thing
我希望它处理 /lib 文件夹中的所有文件。
FILES=$1
for f in $FILES
do
echo "Processing $f file..."
done
目前它只打印第一个文件。如果我使用 $@,它会给我所有的文件,还有我不想要的最后一个参数。有什么想法吗?
【问题讨论】:
如果您要投反对票,那么说明原因的评论会非常有用... 不要这样做:SOURCE_FILES="$@:2"; for FILE in $SOURCE_FILES; do
。如果任何文件名中有空格或全局字符,那将不起作用。你不需要临时变量,所以你可以写:for file in "$@:2"; do
。如果你真的想要一个临时的,使用一个数组而不是一个简单的变量:source_files=("$@:2"); for file in "$source_files[@]"; do
请将您自己的答案添加为单独的答案,而不是对问题进行编辑——这样您的问题就可以被赞成/反对/&c。与您的答案分开。
【参考方案1】:
正如正确指出的,命令行上的lib/*
正在扩展为lib
中的所有文件。为防止扩展,您有 2 个选项。 (1) 引用您的意见:
$ script.sh 'lib/*' ../test_git_thing
或者 (2),关闭文件通配。但是,set -f
选项将禁用 shell 中的路径名扩展,但它将禁用所有路径名扩展(在脚本中设置它并没有帮助,因为扩展是由 shell 在将参数传递给脚本之前完成的)。在您的情况下,最好引用输入或将第一个参数作为目录名称传递,并在脚本中添加扩展:
DIR=$1
for f in "$DIR"/*
【讨论】:
set -f
禁用通配; nullglob
是忽略不匹配模式的 shell 选项,而不是将其视为文字字符串。
没错。谢谢你的收获。
@chepner set -f
尽管在即时脚本方面受到与关闭通配符相同的限制,但如果调用而不是逐个脚本地调用,它会禁用所有路径名扩展。反过来肯定会更容易......【参考方案2】:
当您调用“script.sh lib/*”时,参数列表将在命令行展开,您的脚本将被调用,并将 lib/ 中的所有文件作为参数。由于您只在脚本中引用 $1,因此它只打印第一个文件。您需要在命令行上转义通配符,以便将其传递给您的脚本以执行通配符。
【讨论】:
另一种选择是忽略最后一个参数还是在全局参数周围加上引号? 赞成解释全球正在扩大。理解这一点真的很有帮助。【参考方案3】:在 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 中的子字符串/子序列表示法,而 $@[start,end]
是 zsh 中的子序列。在所有情况下,下标表达式都被计算为算术表达式,这就是$#-1
起作用的原因。 (在zsh中,你需要$#-1
,因为$#-
被解释为“$-
的长度”。)
在所有三个 shell 中,您都可以使用带有标量变量的 $x:start:length
语法来提取子字符串;在 bash 和 ksh 中,您可以使用 $a[@]:start:length
和数组来提取值的子序列。
【讨论】:
接受这个,因为它有助于获得最佳解决方案。我最终更改了顺序,只是忽略了第一个参数,然后将剩余的参数用作文件数组。工作得很好!谢谢。【参考方案4】:这回答了给定的问题,没有使用非 POSIX 功能,也没有解决方法,例如禁用通配符。
您可以使用循环找到最后一个参数,然后在处理文件列表时将其排除。在此示例中,$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 通常会将通配符传递给参数列表。
【讨论】:
以上是关于Bash glob 参数只显示第一个文件而不是所有文件的主要内容,如果未能解决你的问题,请参考以下文章