循环输入并在 bash 中执行命令的通用函数?

循环输入并在 bash 中执行命令的通用函数?

我试图创建一个循环输入并执行命令的函数 - 无论它们如何分隔。

function loop {
    # Args
    #     1: Command
    #     2: Inputs
    for input in "$2" ; do                     
        $1 $input
    done
}
declare -a arr=("1" "2" "3")

$ loop echo "$arr[@]"
1

$ loop echo 1 2 3
1

$ loop echo $arr
1

然而根据答案,for .. in ..适用于数组:

for item in "${arr[@]}" ; do
   echo "$item"
done

它也适用于空格分隔的值:

for item in 1 2 3 ; do
   echo "$item"
done

简而言之,我如何在传递参数时获得"${arr[@]}"和的效果。1 2 3

另外,是否可以将这种循环概念扩展到任何类型的分隔项目,例如\n文件等分隔内容?在Python中我们有一个概念iterators,bash中有类似的东西吗?

答案1

您没有正确调用该数组。 $arr只会扩展到数组中的第一个元素,$arr[@]并将扩展到第一个附加了文字字符串的元素[@]

要调用数组的所有元素,请使用:"${arr[@]}"

您遇到的另一个问题是$2仅包含第二个位置参数,您试图迭代第三个、第四个、第五个等。它们都将存储在$@.

为了实现你的目标,你可以这样做:

function loop {
    local command=$1
    shift
    for i in "$@"; do
        "$command" "$i"
    done
}

这将设置command为第一个位置参数,然后移动,以便$@可以用于循环其余的位置参数。然后你只需要正确调用数组:

$ declare -a arr=("element1" "element2" "element3")
$ loop echo "${arr[@]}"
element1
element2
element3
$ loop printf 'hello ' 'world\n'
hello world
$ loop touch file1 file2 file3
$ ls
file1  file2  file3

如果您希望此函数能够接受各种分隔符,您可以执行以下操作:

function loop {
    local command=$1
    local delim=$2
    shift 2
    set -- $(tr "$delim" ' ' <<<"$@")
    for i in "$@"; do
        "$command" "$i"
    done
}

这意味着您必须通过第二个参数指定使用什么分隔符,例如:

$ loop echo '|' 'one|two|three'
one
two
three
$ loop echo '\n' "$(printf '%s\n' 'one' 'two' 'three')"
one
two
three

然而,这有一些错误(如果您指定自定义分隔符,它仍然会用空格分隔)

答案2

您的代码中有语法错误。在对函数的调用中loop,使用"${arr[@]}"将数组扩展arr到其每个元素的列表(单独引用)。这是您在显示的循环中执行的操作for,也是您在调用该函数时应该执行的操作:

loop echo "${arr[@]}"

另请注意,您的函数需要选择您传递的命令的名称,然后需要循环遍历其剩余参数:

loop () {
    local cmd=$1; shift
    for arg do
        "$cmd" "$arg"
    done
}

在这里,我们将第一个参数分配给变量cmd(这是命令),然后我们shift将该参数从参数列表中删除。参数列表现在仅包含您想要循环的字符串。

然后循环依次调用每个剩余参数的命令。

这以有限的方式复制了该实用程序的用途xargs,并且可以使用该实用程序重新实现该函数(除非您期望其中一个参数将包含嵌入的换行符,在这种情况下,您必须将选项调整为xargs) :

loop () {
    local cmd=$1; shift
    printf '%s\n' "$@" | xargs -L 1 "$cmd"
}

由于该xargs实用程序希望通过标准输入传递数据,我们安排使用printf并将给出的每个参数打印loop在其自己的行上(同时告诉xargs对使用的每个换行符分隔的参数调用一次给定的实用程序-L 1)。

这将允许我们使用 的某些实现的其他功能,例如使用where是要并行运行的一些进程数来xargs启动并行进程。-P nn

测试:

$ cat script.sh
loop () {
    local cmd=$1; shift
    printf '%s\n' "$@" | xargs -L 1 "$cmd"
}

arr1=(1 2 3)
arr2=("hello world" "home sweet home")

loop echo "${arr1[@]}"
loop echo "${arr2[@]}"
$ bash script.sh
1
2
3
hello world
home sweet home

答案3

您的循环与 GNU Parallel 非常相似:

declare -a arr=("1 a" "2 b" "3 c")
var1="1 a,2 b,3 c"
var2="1-a 2-b 3-c"
parallel -j1 echo ::: "${arr[@]}"
parallel -j1 -d , echo ::: "$var1"
parallel -j1 echo ::: $var2

-j1强制一次运行一个作业。

相关内容