简体   繁体   English

Bash glob参数仅显示第一个文件,而不显示所有文件

[英]Bash glob parameter only shows first file instead of all files

I want to run this cmd line script 我想运行此cmd行脚本

$ script.sh   lib/* ../test_git_thing

I want it to process all the files in the /lib folder. 我希望它处理/ lib文件夹中的所有文件。

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

Currently it only prints the first file. 当前它仅打印第一个文件。 If I use $@, it gives me all the files, but also the last param which I don't want. 如果我使用$ @,它会给我所有文件,但会提供我不需要的最后一个参数。 Any thoughts? 有什么想法吗?

As correctly noted, lib/* on the command line is being expanded into all files in lib . 正确地指出,命令行上的lib/*被扩展为lib所有文件。 To prevent expansion, you have 2 options. 为防止扩展,您有2个选项。 (1) quote your input: (1)引用您的输入:

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

Or (2), turn file globbing off. 或(2),关闭文件滚动。 However, the option set -f will disable pathname expansion within the shell, but it will disable all pathname expansion (setting it within the script doesn't help as expansion is done by the shell before passing arguments to your script). 但是,选项set -f将禁用外壳程序内的路径名扩展,但将禁用所有路径名扩展(在脚本内进行设置无济于事,因为扩展是在将参数传递给脚本之前由外壳程序完成的)。 In your case, it is probably better to quote the input or pass the first arguments as a directory name, and add the expansion in the script: 在您的情况下,最好将输入引号或将第一个参数作为目录名传递,然后在脚本中添加扩展名:

DIR=$1
for f in "$DIR"/*

The argument list is being expanded at the command line when you invoke "script.sh lib/*" your script is being called with all the files in lib/ as args. 当您调用“ script.sh lib / *”时,将在命令行中扩展参数列表,您的脚本将以lib /中的所有文件作为args进行调用。 Since you only reference $1 in your script, it's only printing the first file. 由于您仅在脚本中引用$ 1,因此仅打印第一个文件。 You need to escape the wildcard on the command line so it's passed to your script to perform the globbing. 您需要在命令行上对通配符进行转义,以便将通配符传递到脚本中以进行通配。

In bash and ksh you can iterate through all arguments except the last like this: 在bash和ksh中,您可以遍历除最后一个参数外的所有参数,如下所示:

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

In zsh, you can do something similar: 在zsh中,您可以执行类似的操作:

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

$# is the number of arguments and ${@:start:length} is substring/subsequence notation in bash and ksh, while $@[start,end] is subsequence in zsh. $#是参数的数量, ${@:start:length}是bash和ksh中的子字符串/子序列符号,而$@[start,end]是zsh中的子序列。 In all cases, the subscript expressions are evaluated as arithmetic expressions, which is why $#-1 works. 在所有情况下,下标表达式都被视为算术表达式,这就是$#-1起作用的原因。 (In zsh, you need ${#}-1 because $#- is interpreted as "the length of $- ".) (在zsh中,您需要${#}-1因为$#-被解释为“ $-的长度”。)

In all three shells, you can use the ${x:start:length} syntax with a scalar variable, to extract a substring; 在所有三个shell中,可以将${x:start:length}语法与标量变量一起使用,以提取子字符串; in bash and ksh, you can use ${a[@]:start:length} with an array to extract a subsequence of values. 在bash和ksh中,可以将${a[@]:start:length}与数组一起使用以提取值的子序列。

This answers the question as given, without using non-POSIX features, and without workarounds such as disabling globbing. 这可以回答给定的问题,而无需使用非POSIX功能,也不需要解决方法(例如禁用全局)。

You can find the last argument using a loop, and then exclude that when processing the list of files. 您可以使用循环找到最后一个参数,然后在处理文件列表时将其排除在外。 In this example, $d is the directory name, while $f has the same meaning as in the original answer: 在此示例中, $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

Additionally, it would be a good idea to also test if "$f" is a file, since it is common for shells to pass the wildcard character through the argument list if no match is found. 另外,最好测试一下"$f"是否是一个文件,因为如果找不到匹配项,shell通常会将通配符通过参数列表传递。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM