我试图创建一个循环输入并执行命令的函数 - 无论它们如何分隔。
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 n
n
测试:
$ 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
强制一次运行一个作业。