Bash 如何处理--&引用;在shell脚本参数中?

Bash 如何处理--&引用;在shell脚本参数中?,bash,shell,zsh,sh,Bash,Shell,Zsh,Sh,这个问题有三个部分,每个部分都很简单,但结合起来并不是一件小事(至少对我来说是这样):) 需要编写一个脚本,该脚本的参数应为: 另一个命令的一个名称 命令的几个参数 文件清单 示例: ./my_script head -100 a.txt b.txt ./xxx/*.txt ./my_script sed -n 's/xxx/aaa/' *.txt 等等 在我的脚本中,出于某种原因,我需要区分 命令是什么 命令的参数是什么 档案是什么 因此,编写上述示例的最标准方式可能是: ./my_sc

这个问题有三个部分,每个部分都很简单,但结合起来并不是一件小事(至少对我来说是这样):)

需要编写一个脚本,该脚本的参数应为:

  • 另一个命令的一个名称
  • 命令的几个参数
  • 文件清单
  • 示例:

    ./my_script head -100 a.txt b.txt ./xxx/*.txt
    ./my_script sed -n 's/xxx/aaa/' *.txt
    
    等等

    在我的脚本中,出于某种原因,我需要区分

    • 命令是什么
    • 命令的参数是什么
    • 档案是什么
    因此,编写上述示例的最标准方式可能是:

    ./my_script head -100 -- a.txt b.txt ./xxx/*.txt
    ./my_script sed -n 's/xxx/aaa/' -- *.txt
    
    问题1:这里有更好的解决方案吗

    在./my_脚本中处理(首次尝试):

    文件名
    包含
    空格
    和/或“--”时,此解决方案将失败,例如
    /一些--path/to/more/idiotic file name.txt

    问题2:如何正确获取
    $command
    $args
    $filenames
    以供以后执行

    echo $filenames | $command $args #but want one filename = one line (like ls -1)
    
    问题3:-如何实现以下执行风格

    echo $filenames | $command $args #but want one filename = one line (like ls -1)
    

    这是一个很好的shell解决方案,还是需要使用例如perl?

    问题1

    我不这么认为,至少如果您需要对任意命令执行此操作,就不会

    问题3

    command=$1
    shift
    while [ $1 != '--' ]; do
        args="$args $1"
        shift
    done
    shift
    while [ -n "$1" ]; do
        echo "$1"
        shift
    done | $command $args
    
    问题2


    这与问题3有什么不同?

    首先,听起来您正在尝试编写一个脚本,该脚本接受一个命令和一个文件名列表,然后依次对每个文件名运行该命令。这可以在bash中的一行中完成:

    $ for file in a.txt b.txt ./xxx/*.txt;do head -100 "$file";done $ for file in *.txt; do sed -n 's/xxx/aaa/' "$file";done 如果要为剩余的每个参数运行命令,它将如下所示:

    for target in "$@";do
         eval $command \"$target\"
    done
    
    while read target; do
         eval $command \"$target\"
    done
    
    parameterFlag=""
    for parameter in "$@"     #Quotes are important!
    do
        if [[ "x${parameter}" == "x${parameter#-}" ]]
        then
             parameterFlag="Tripped!"
        fi
        if [[ "x${parameter}" == "x--" ]]
        then
             print "Parameter \"$parameter\" ends the parameter list"
             parameterFlag="TRIPPED!"
        fi
        if [ -n $parameterFlag ]
        then
            print "\"$parameter\" is a file"
        else
            echo "The parameter \"$parameter\" is a parameter"
        fi
    done
    
    如果您想从STDIN读取文件名,它看起来更像这样:

    for target in "$@";do
         eval $command \"$target\"
    done
    
    while read target; do
         eval $command \"$target\"
    done
    
    parameterFlag=""
    for parameter in "$@"     #Quotes are important!
    do
        if [[ "x${parameter}" == "x${parameter#-}" ]]
        then
             parameterFlag="Tripped!"
        fi
        if [[ "x${parameter}" == "x--" ]]
        then
             print "Parameter \"$parameter\" ends the parameter list"
             parameterFlag="TRIPPED!"
        fi
        if [ -n $parameterFlag ]
        then
            print "\"$parameter\" is a file"
        else
            echo "The parameter \"$parameter\" is a parameter"
        fi
    done
    

    $@
    变量在被引用时将能够按其应有的方式对参数进行分组:

    for parameter in "$@"
    do
        echo "The parameter is '$parameter'"
    done
    
    如果给出:

    head -100 test this "File name" out
    
    将打印

    the parameter is 'head'
    the parameter is '-100'
    the parameter is 'test'
    the parameter is 'this'
    the parameter is 'File name'
    the parameter is 'out'
    
    现在,您所要做的就是解析循环。您可以使用一些非常简单的规则:

  • 第一个参数始终是文件名
  • 后面以破折号开头的参数是参数
  • 在“-”之后,或者一个文件名没有以“-”开头,其余的都是文件名
  • 您可以使用以下命令检查参数中的第一个字符是否为破折号:

    if [[ "x${parameter}" == "x${parameter#-}" ]]
    
    如果您以前没有见过这种语法,那么它是一个左过滤器。
    #
    将变量名的两部分分开。第一部分是变量的名称,第二部分是要截断的glob过滤器(不是正则表达式)。在这种情况下,它是一个单破折号。只要这句话不是真的,你就知道你有一个参数。顺便说一句,在这种情况下,
    x
    可能需要,也可能不需要。当您运行测试时,如果您有一个带破折号的字符串,测试可能会将其误认为是测试的参数,而不是值

    把它放在一起会是这样的:

    for target in "$@";do
         eval $command \"$target\"
    done
    
    while read target; do
         eval $command \"$target\"
    done
    
    parameterFlag=""
    for parameter in "$@"     #Quotes are important!
    do
        if [[ "x${parameter}" == "x${parameter#-}" ]]
        then
             parameterFlag="Tripped!"
        fi
        if [[ "x${parameter}" == "x--" ]]
        then
             print "Parameter \"$parameter\" ends the parameter list"
             parameterFlag="TRIPPED!"
        fi
        if [ -n $parameterFlag ]
        then
            print "\"$parameter\" is a file"
        else
            echo "The parameter \"$parameter\" is a parameter"
        fi
    done
    

    这需要一段时间才能正确回答。1.为什么要用背部抽搐?它们已经被弃用了,除非您希望以root用户身份在Solaris或AIX上做大量工作,否则没有必要这样做。使用$(cmd)!2.(更多关于您的问题),请阅读getopt和getopts(用于shell)。我在这里看到了很多例子,至少有一个非常详细。再加上像
    while($#>0))这样的东西做processArgs;转移;完成
    将有所帮助。3.考虑使用case语句。对于
    --
    您可能必须对其进行转义或引用,或将其转换为类似“[-][-]”的charclass。如果文件名包含空格,echo$filenames | sed-将中断。e、 g.echo“file1.txt”“file2 with spaces.txt”-因此询问-我的第一次尝试是错误的。@TomShaw:第一篇文章不错。对于最后一块代码,可能需要在循环内部加上
    eval
    。感谢您做了大量的列表。;-)@谢:谢谢你的反馈!这一行
    filenames=$*
    在本来是好代码的代码中是非常糟糕的。一般来说,不要使用
    $*
    。特别是在这种情况下,不要使用
    $*
    。如果使用Bash数组,则“$@”就可以了。实际上,循环最好写为“$@”中目标的
    ;做完成
    。如果文件名实际上不包含空格,那么
    $*
    就可以了。只要名称中有空格,
    $*
    就会处理错误。谢谢@Jonathan,我已经加入了你的修复程序
    x
    破解只适用于古代贝壳。请不要使用它,它会让代码变得丑陋。@nyuszika7h,……好吧——在现代shell中也有一些角落的情况需要它,但这些情况只存在于
    []
    (而不是
    [[[]]
    )中,并且当
    test
    传递了三个以上的参数时(因此,当使用
    -a
    -o
    时)。。。这就是为什么最新版本的POSIX将它们描述为不推荐使用的部分原因。