【问题标题】:bash script to perform operation on each argument against the final argumentbash 脚本针对最终参数对每个参数执行操作
【发布时间】:2009-09-22 17:50:46
【问题描述】:

假设您要创建一个不支持任何选项但行为类似于cpbash 脚本,因为您的系统提供的cp 不接受多个来源。

系统的cp的用法(假设的和损坏的)是:

cp source target    # target may be a directory

脚本的用途是:

cp.sh source... target    # target must be a directory

这是脚本的起点:

#!/bin/bash
tgt="$1"
shift
for src in "$@"; do
    echo cp $src $tgt
done

当使用参数“a b c d”运行时(注意d 是目标),它会输出:

cp b a
cp c a
cp d a

我们的目标是修复脚本以输出它,同时保持代码简单:

cp a d
cp b d
cp c d

【问题讨论】:

    标签: bash arguments shift


    【解决方案1】:

    /test.bash source1 source2 target1

    #!/bin/bash
    
    target=${!#} 
    
    if [ ! -d $target ] ; then
        echo "$target must be a directory " >&2
        exit 1;
    fi
    
    args=("$@")
    unset args[${#args[@]}-1]
    
    for src in "${args[@]}"; do
        echo cp $src $target
    done
    

    会输出

    cp source1 target1
    cp source2 target1
    

    【讨论】:

    • 我真的很喜欢这个!我在 $target 和 $src 的使用周围添加引号(以支持带空格的路径),但除此之外这非常完美。
    • 您应该在for 循环中添加一个if [ -f "$src" ](或-a)。
    【解决方案2】:

    您可以使用数组切片来省略最后一个参数:

    tgt="${!#}"
    for src in "${@:1:$#-1}"; do
        cp "$src" "$tgt"
    done
    

    【讨论】:

      【解决方案3】:
      #!/bin/bash
      tgt="${@: -1}"    # get the last parameter
      for src in "$@"; do
          if [[ $src != $tgt ]]; then
              echo cp "$src" "$tgt"
          fi
      done
      

      【讨论】:

      • 不错,但我更喜欢删除最后一个参数的“未设置”方法而不是此处使用的比较(想象一下,如果源实际上与此代码的目标相等,会发生什么情况)。
      • 但你不会想听这个的,对吧? "cp:d' and d' 是同一个文件"
      • 或"cp: 省略目录`d'"
      【解决方案4】:

      为什么? cp 命令已经这样做了。做一个 ma​​n cp 你会看到的。

      如果你仍然坚持,这里有两种方法可以得到最后一个参数。方法一:将命令行放入数组中,提取最后一个元素:

      arg=("$@")
      last_arg=${arg[(($# - 1))]}
      

      第一行将命令行参数放入数组 arg 中。如果你的命令行包含 a b c d 那么 arg[0] == 'a', ... argv[3] == 'd'。

      第二行提取最后一个参数。 (($# - 1)) 接受参数的数量(在这种情况下为 4),从中减去 1(得到 3)。然后该表达式变为:

      last_arg=${arg[3]}
      

      指向最后一个参数。


      第二种方法不是很便携,它使用了 BASH_ARGV 变量,它是 $@,但顺序相反。如果你的命令行是 a b c d 那么 ${BASH_ARGV[0]} == 'd', ... ${BASH_ARGV[3]} == 'a':

      last_arg=${BASH_ARGV[0]}
      

      我希望这会有所帮助。

      【讨论】:

      • 当然“cp”已经支持这种行为。我还有另一个程序要运行,但是没有。
      【解决方案5】:
      #!/bin/bash
      t=(x "$@")
      i=1; while [ $i -lt $# ]; do
        echo cp ${t[$i]} ${t[$#]}
        i=$(($i + 1))
      done
      

      【讨论】:

      • 不要使用$*;它不会在参数中保留空格(它会生成额外的参数)。使用"$@" - 几乎总是。
      • 没错,我在链接命令时总是使用“$@”,不知道为什么我没有在这里这样做......
      【解决方案6】:

      您可以直接执行此操作,而无需使用 xargs 编写脚本:

      echo source1 source2 | tr "\n" "\0" | tr " " "\0" | 
         xargs --verbose -0 -I{} cp {} dest
      

      【讨论】:

        【解决方案7】:

        您不需要任何特定于 bash 的功能:

        评估最后=\${$#} 而 [ $# -gt 1 ];做 回声“cp $1 $last” 转移 完毕

        【讨论】:

        • 这依赖于 300 美元作为第 300 个参数(当有 300 个参数时)。它不适用于所有 Bourne shell(例如 Solaris 10 /bin/sh:set -- a b c d e f g h i j k l; echo $10 生成 'a0' 而不是 'j'。
        • Korn shell 同上(同样,在 Solaris 10 上)。
        • 当然,对于 $# > 9,您需要使用 ${N}(例如 ${10}、${300})。这对我来说在 Solaris 10 上运行良好,使用 ksh、bash、/bin/sh。
        【解决方案8】:

        用这个来提取最后一个参数:

        评估 tgt=\$$#

        然后以与您相同的方式处理,当您点击 $# 时退出循环

        这是 whjole 脚本:

        eval tgt=\$$#
        
        for src in $@
        do
         if [ "$src" == "$tgt" ];
         then
            exit
        fi
        echo cp $src $tgt
        done
        

        如果你问我,很简单!

        【讨论】:

        • 这种方法胜过我的手。向你致敬。
        • 当 $# 被击中时停止的部分需要另外跟踪循环索引,我不太关心。正如其他一些答案所示,eval 不是必需的。
        • 您不需要索引,只需将当前值与 tgt 进行比较即可。我所说的在你达到 $# 时退出的意思是当你达到 $# 的值时退出
        猜你喜欢
        • 1970-01-01
        • 2011-01-28
        • 2014-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多