这个问题的灵感来自bash nested variable in for loop。
如果我在bash中有一个数组,并且我希望能够为该数组的每个元素运行一个任意命令,那么有没有办法通过泛型函数来实现,而不是使用循环?那是:
dest_array=( host1:/foo host2:/bar host3:/baz )
copy ./file dest_array
每个扩展称为:
copy ./file host1:/foo
copy ./file host2:/bar
copy ./file host3:/baz
更好的是,有没有办法为多个阵列执行此操作?例如:
sources=( first second )
dests=( host1:/foo host2:/bar )
copy sources dests
调用(没有特定的顺序):
copy first host1:/foo
copy first host2:/bar
copy second host1:/foo
copy second host2:/bar
考虑以下函数,为bash 4.3或更高版本编写:
run_for_each() {
local -n _items=$1; shift
local sigil=$1; shift
local -a args=( "$@" )
local -a call
local retval=0
for item in "${_items[@]}"; do
call=( "${args[@]//$sigil/$item}" )
"${call[@]}" || (( retval |= $? ))
done
return "$retval"
}
作为使用示例:
sources=( first second )
dests=( host1:/foo host2:/bar )
run_for_each sources SOURCE \
run_for_each dests DEST \
rsync -Pv SOURCE DEST
如果你想让它并发,那可能看起来像:
run_for_each_concurrent() {
local -n _items=$1; shift
local sigil=$1; shift
local -a args=( "$@" )
local -a pids=( )
local -a call
local retval=0
for item in "${_items[@]}"; do
call=( "${args[@]//$sigil/$item}" )
"${call[@]}" & pids+=( "$!" )
done
for pid in "${pids[@]}"; do
wait "$pid" || (( retval |= $? ))
done
return "$retval"
}
...每个数组条目将同时运行一个进程;等他们全部退出;并返回所有这些子进程的ORed-together退出状态。
顺便说一句 - 如果你没有bash 4.3,可以通过替换以下行来使上述版本适用于旧版本:
local -n _items=$1; shift
改为:
printf -v cmd 'local -a _items=( "${%q[@]}" )' "$1" && eval "$cmd"; shift
您如何看待这个解决方案:
run_for() {
local -n _sources=$1; shift
local src_label=$1; shift
local -n _dests=$1; shift
local dst_label=$1; shift
local -a cmd=( "$@" ) execute
local retval=0
for src_item in "${_sources[@]}"; do
for dst_item in "${_dests[@]}"; do
execute=()
for cmd_item in "${cmd[@]}"; do
case $cmd_item in
$src_label) execute+=("$src_item") ;;
$dst_label) execute+=("$dst_item") ;;
*) execute+=("$cmd_item") ;;
esac
done
"${execute[@]}" || (( retval |= $? ))
done
done
return "$retval"
}
此解决方案也适用于bash
4.3或更高版本,并使用for
循环3次(不是很漂亮)。但是,它具有更直接的用法并正确处理以下情况:
sources=( first second DEST )
dests=( host1 host2 host3 )
run_for sources SOURCE dests DEST echo "<<" SOURCE - DEST ">>"
此解决方案无法处理以下内容:
run_for sources SOURCE dests DEST echo aaSOURCE - DESTaa
虽然,我不会真的认为aaSOURCE
是一个有效的标签。如果是,在我看来,以下内容可以复制您的解决方案的行为,同时仍然保留更直接的用法。当其中一个源等于$dst_label
时,它也会有同样的缺点:
for src_item in "${_sources[@]}"; do
tmp=("${cmd[@]//$src_label/$src_item}")
for dst_item in "${_dests[@]}"; do
execute=("${tmp[@]//$dst_label/$dst_item}")
"${execute[@]}" || (( retval |= $? ))
done
done