Bash脚本:不使用$@或$*扩展参数

4

使用$@可以在bash中对文件列表执行操作。例如:

script.sh:

#!/bin/bash
list=$@
for file in $list; do _commands_; done

然后我可以使用以下方式调用此程序:
~/path/to/./script dir1/{subdir1/*.dat,subdir2/*}

这个参数将会扩展成一系列参数,成为$list。但现在我想要其他的参数,比如$1、$2,以及这个列表中的$3。所以我希望dir1/{subdir1/*.dat,subdir2/*}的扩展发生在脚本内部,而不是变成多个参数。在命令行中,您可以这样做:
find dir1/{subdir1/*.dat,subdir2/*}

我希望得到所需的输出,即文件列表。因此,我尝试了以下方法:

arg1=$1
arg2=$2
list=$(find $3)
for file in $list; do _commands_; done
...

调用:

~/path/to/./script arg_1 arg_2 'dir1/{subdir1/*.dat,subdir2/*}'

但是没有成功。如何使此列表扩展为脚本内的变量的帮助将不胜感激!:)
编辑:因此,以下答案提供了使用以下命令的解决方案:
arg1="$1"
arg2="$2"
shift 2

for f in "$@"; do echo "processing $f"; done;

但出于好奇,是否仍然可以将字符串dir1/{subdir1/*.dat,subdir2/*}传递给脚本内的find命令(或任何实现相同目的的方法),而不使用$@,以此方式获取列表?这可能很有用,例如如果希望将列表作为非第一个或最后一个参数,或者在某些其他情况下,即使需要转义字符或引用参数。

3
list=$@并不能将$@复制到数组中。使用list=( "$@" )即可,但需要扩展为for file in "${list[@]}"。如果反过来做,当输入包含通配符、空格等内容时,就会出现细微的(或明显的)错误。 - Charles Duffy
该列表将是带有相当好的文件名的数据文件,因此这不应该成为问题。虽然在调用gnuplot绘制列出的文件时使用了$list,在单个绘图中绘制列出的文件。格式现在运作良好。 使用 plot for [file in '$list'] file ... - Jonatan Öström
听起来现在这对你的需求很有效。但我建议你小心不要养成这个习惯,因为我曾经见过因为有人假定一个目录只会有好名字而删除了TB级别的备份,然后缓冲区溢出创建了一个带有星号和空格的文件。 - Charles Duffy
2个回答

4
您可以在您的脚本中使用以下代码:
arg1="$1"
arg2="$2"
shift 2

for f in "$@"; do echo "processing $f"; done;

然后按如下方式调用:
~/path/to/script arg_1 arg_2 dir1/{subdir1/*.dat,subdir2/*}

使用 shift 2 命令将位置参数向左移动 2 个位置,从而使得 $3 对应到 $1$4 对应到 $2,以此类推。然后,您可以直接调用 $@ 迭代剩余的参数。

根据 help shift 命令:

shift: shift [n]

  Shift positional parameters.

  Rename the positional parameters $N+1,$N+2 ... to $1,$2 ...  If N is

1
这看起来很完美,命令行参数中不需要转义字符或引号。我之前不熟悉shift命令,非常感谢! - Jonatan Öström

0

Shell扩展是由Shell在调用脚本之前执行的。这意味着您必须引用/转义参数。在脚本中,您可以使用eval执行扩展。

#!/bin/bash
arg1="$1" ; shift
arg2="$2" ; shift
eval "list=($@)"
for q in "${list[@]}" ; do echo "$q" ; done

$ ./a 123 456 'a{b,c}' 'd*'
ab ac d.pl docs

我不明白为什么要在你的示例脚本内进行扩展。

#!/bin/bash
arg1="$1" ; shift
arg2="$2" ; shift
list=("$@")
for q in "${list[@]}" ; do echo "$q" ; done

或者只是

#!/bin/bash
arg1="$1" ; shift
arg2="$2" ; shift
for q in "$@" ; do echo "$q" ; done

$ ./a 123 456 a{b,c} d*
ab
ac
d.pl
docs

1
如果参数中包含分号会怎么样?使用 eval 解析命令行参数是危险的,而您并没有表明这一点。 - user4453924
是的,你说得对。脚本内部不需要扩展。我错误地假设那将是必要的:) 非常感谢!虽然已经检查了第一个答案;) - Jonatan Öström
我没有错(尽管由于转移我认为我错了)。好的,将其作为第三个参数传递,如 "1);ls;x=(1",看看会发生什么。 - user4453924
1
不是因为它们被引用了。将“"1);ls;x=(1"”作为第三个参数传递给anubhava的脚本和您的脚本,并查看差异。 - user4453924
1
我想要的只是你在回答中警告可能会发生这种情况,以便有人不会意外地(最坏的情况下)毁掉他们的系统。 - user4453924
@JID,你真的没有抓住重点。OP问如何使script2 '$( echo foo >&2 )'script1 $( echo foo >&2 )的工作方式相同。当然,bash命令可能会破坏一个人的系统。你认为需要提到这一点吗? - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接