当前位置:  开发笔记 > 编程语言 > 正文

如何在Bash脚本中迭代参数

如何解决《如何在Bash脚本中迭代参数》经验,为你挑选了5个好方法。

我有一个复杂的命令,我想制作一个shell/bash脚本.我可以$1轻松地写出来:

foo $1 args -o $1.ext

我希望能够将多个输入名称传递给脚本.什么是正确的方法呢?

当然,我想处理其中包含空格的文件名.



1> Robert Gambl..:

使用"$@"来代表所有的参数:

for var in "$@"
do
    echo "$var"
done

这将迭代每个参数并在单独的行上打印出来.$ @的行为类似于$*,但是当引用时,如果参数中有空格,则它们会被正确分解:

sh test.sh 1 2 '3 4'
1
2
3 4


顺便说一下,"$ @"`的另一个优点是,当没有位置参数时,它会扩展为空,而"$*"`扩展为空字符串 - 是的,没有参数之间存在差异还有一个空洞的论点.请参阅`/bin/sh`](http://stackoverflow.com/questions/154625/)中的[`$ {1:+"$ @"}.
请注意,此表示法也应该在shell函数中用于访问函数的所有参数.
@ m4l490n:`shift`丢弃`$ 1`,并向下移位所有后续元素。

2> Jonathan Lef..:

重写现在缺失的答案被VonC.

Robert Gamble的简洁回答直接涉及这个问题.这个放大了包含空格的文件名的一些问题.

另请参阅:/ bin/sh中的$ {1:+"$ @"}

基本论点: "$@"是正确的,$*(未引用)几乎总是错误的.这是因为"$@"当参数包含空格时工作正常,并且与$*它们不包含空格时的工作方式相同.在某些情况下,"$*"也可以,但"$@"通常(但不总是)在同一个地方工作.未加引号,$@并且$*是等价的(而且几乎总是错的).

那么,是什么样的区别$*,$@,"$*",和"$@"?它们都与'shell的所有参数'有关,但它们做了不同的事情.什么时候不引用,$*$@做同样的事情.他们将每个'单词'(非空白的序列)视为一个单独的参数.引用的表单完全不同,但是:"$*"将参数列表视为单个空格分隔的字符串,而"$@"对参数的处理几乎与在命令行中指定时完全相同. "$@"当没有位置参数时,它会扩展到任何东西; "$*"扩展为一个空字符串 - 是的,这是有区别的,虽然很难察觉它.在引入(非标准)命令后,请参阅下面的更多信息al.

次要论文:如果你需要用空格处理参数然后将它们传递给其他命令,那么你有时需要非标准的工具来帮助.(或者你应该谨慎使用数组:"${array[@]}"行为类似于"$@".)

例:

    $ mkdir "my dir" anotherdir
    $ ls
    anotherdir      my dir
    $ cp /dev/null "my dir/my file"
    $ cp /dev/null "anotherdir/myfile"
    $ ls -Fltr
    total 0
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir/
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir/
    $ ls -Fltr *
    my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ ls -Fltr "./my dir" "./anotherdir"
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ var='"./my dir" "./anotherdir"' && echo $var
    "./my dir" "./anotherdir"
    $ ls -Fltr $var
    ls: "./anotherdir": No such file or directory
    ls: "./my: No such file or directory
    ls: dir": No such file or directory
    $

为什么不起作用?它不起作用,因为shell在扩展变量之前处理引号.所以,要让shell注意嵌入的引号$var,你必须使用eval:

    $ eval ls -Fltr $var
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ 

当你有文件名如" He said, "Don't do this!""(带引号和双引号和空格)时,这会变得非常棘手.

    $ cp /dev/null "He said, \"Don't do this!\""
    $ ls
    He said, "Don't do this!"       anotherdir                      my dir
    $ ls -l
    total 0
    -rw-r--r--   1 jleffler  staff    0 Nov  1 15:54 He said, "Don't do this!"
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir
    $ 

shell(所有这些)都不会使这些东西特别容易处理,所以(有趣的是)很多Unix程序都不能很好地处理它们.在Unix上,文件名(单个组件)可以包含除斜杠和NUL之外的任何字符'\0'.但是,shell强烈建议路径名中的任何位置都不要有空格或换行符或制表符.这也是标准Unix文件名不包含空格等的原因.

处理可能包含空格和其他麻烦字符的文件名时,你必须非常小心,我很久以前就发现我需要一个在Unix上不标准的程序.我称之为escape(版本1.1的日期为1989-08-23T16:01:45Z).

这是一个escape使用的例子- 使用SCCS控制系统.这是一个封面脚本,同时执行delta(思考签到)和 get(认为签出).各种论点,特别是-y(你进行更改的原因)将包含空格和换行符.请注意,该脚本的日期是1992年,因此它使用反向$(cmd ...)标记而不是 符号,并且不在#!/bin/sh第一行使用.

:   "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
#   Delta and get files
#   Uses escape to allow for all weird combinations of quotes in arguments

case `basename $0 .sh` in
deledit)    eflag="-e";;
esac

sflag="-s"
for arg in "$@"
do
    case "$arg" in
    -r*)    gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    -e)     gargs="$gargs `escape \"$arg\"`"
            sflag=""
            eflag=""
            ;;
    -*)     dargs="$dargs `escape \"$arg\"`"
            ;;
    *)      gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    esac
done

eval delta "$dargs" && eval get $eflag $sflag "$gargs"

(这些天我可能不会非常彻底地使用escape - -e例如,参数不需要它- 但总的来说,这是我使用的更简单的脚本之一escape.)

escape程序只是输出它的参数,而不是像echo 它一样,但它确保参数受到保护以供使用 eval(一级eval;我确实有一个程序执行远程shell执行,并且需要转义输出escape).

    $ escape $var
    '"./my' 'dir"' '"./anotherdir"'
    $ escape "$var"
    '"./my dir" "./anotherdir"'
    $ escape x y z
    x y z
    $ 

我有另一个程序叫做al每行一个列出它的参数(它更古老:版本1.1日期1987-01-27T14:35:49).它在调试脚本时最有用,因为它可以插入命令行以查看实际传递给命令的参数.

    $ echo "$var"
    "./my dir" "./anotherdir"
    $ al $var
    "./my
    dir"
    "./anotherdir"
    $ al "$var"
    "./my dir" "./anotherdir"
    $

[ 补充: 现在为了显示各种"$@"符号之间的区别,这里还有一个例子:

$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh     *      */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$

请注意,没有任何内容保留命令行**/*命令行之间的原始空白.另请注意,您可以使用以下命令更改shell中的"命令行参数":

set -- -new -opt and "arg with space"

这设置了4个选项,' -new',' -opt',' and'和' arg with space'.
]

嗯,这是一个相当长的答案 - 也许解释是更好的术语.escape可根据要求提供的源代码(通过电子邮件发送到gmail dot com的firstname dot lastname).源代码al非常简单:

#include 
int main(int argc, char **argv)
{
    while (*++argv != 0)
        puts(*argv);
    return(0);
}

就这样.它相当于test.shRobert Gamble所展示的脚本,并且可以编写为shell函数(但是当我第一次编写时,本地版本的Bourne shell中不存在shell函数al).

另请注意,您可以编写al为简单的shell脚本:

[ $# != 0 ] && printf "%s\n" "$@"

需要条件,以便在不传递参数时不产生输出.该printf命令将生成一个只有格式字符串参数的空行,但C程序不生成任何内容.


很长的帖子...但我仍然赞成,因为当你实现使用输入参数的东西时,必须阅读.

3> Alok Singhal..:

请注意,罗伯特的答案是正确的,它也适用sh.您可以(便携地)进一步简化它:

for i in "$@"

相当于:

for i

即,你什么都不需要!

测试($是命令提示符):

$ set a b "spaces here" d
$ for i; do echo "$i"; done
a
b
spaces here
d
$ for i in "$@"; do echo "$i"; done
a
b
spaces here
d

我首先在Kernighan和Pike的Unix编程环境中读到过这个.

bash,help for文件中:

for NAME [in WORDS ... ;] do COMMANDS; done

如果'in WORDS ...;'不存在,则'in "$@"'假定.


我并没有断言`for i`更好,因为它可以节省击键次数.我比较了'for i`和`for i in"$ @"`的可读性.
很高兴知道阅读其他人的脚本.太糟糕了.明确的方式更好.避免混淆!
大多数写bash的人都会知道'$ @`是什么,如果没有,你很容易搜索...你认为有人会知道如何用`for i`*迭代args而不用了解`$ @`先?不.知道`for i`的人也知道`$ @`,但不是反过来,所以要明确.
我不同意.人们必须知道神秘的"$ @"是什么意思,一旦你知道`for i`意味着什么,它的可读性就不比`for @ in"$ @"`.
我讨厌使用你的代码.保存一些按键不会使事情明确和可读.在贝壳是新的时代,千字节很重要.不再.
@uchuugaka这是明确的:...`而不是`i`。我喜欢这个主意。

4> nuoritoveri..:

对于简单的情况,您也可以使用shift.它将参数列表视为队列,每个shift抛出第一个参数,剩下的每个参数的数量递减.

#this prints all arguments
while test $# -gt 0
do
    echo "$1"
    shift
done


如果你需要一些参数值,这个解决方案会更好,例如`--file myfile.txt`所以,$ 1是参数,$ 2是值,当你需要跳过另一个参数时你调用shift两次
请注意,您应该使用`echo"$ 1"`来保留参数字符串值的间距.此外,(一个小问题)这是破坏性的:如果您将它们全部移开,则无法重复使用这些参数.通常,这不是问题 - 您只需处理参数列表一次.

5> 小智..:

您还可以将它们作为数组元素进行访问,例如,如果您不想迭代所有这些元素

argc=$#
argv=("$@")

for (( j=0; j


我认为argv =($ @)行应为argv =(“ $ @”)其他带有空格的明智args,无法正确处理
推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有