考虑以下动态变量,它们每次都可能发生变化。有时甚至可能有5个变量,但所有变量的长度每次都是相同的。
var1='a b c d e... upto z'
var2='1 2 3 4 5... upto 26'
var3='I II III IV V... upto XXVI'
我正在寻找一种通用的方法来迭代for循环中的变量&我的期望输出值应该像下面。
a,1,I
b,2,II
c,3,III
d,4,IV
e,5,V
.
.
goes on upto
z,26,XXVI
如果我使用嵌套循环,那么我得到所有可能的组合并不是预期的结果。
另外,我知道如何使用for循环和shift使用下面的链接为2个变量工作https://unix.stackexchange.com/questions/390283/how-to-iterate-two-variables-in-a-sh-script
与paste
paste -d , <(tr ' ' 'n' <<<"$var1") <(tr ' ' 'n' <<<"$var2") <(tr ' ' 'n' <<<"$var3")
a,1,I
b,2,II
c,3,III
d,4,IV
e...z,5...26,V...XXVI
但是显然必须为更多的varN
添加其他参数替换是不可扩展的。
你需要"一次两个变量
var1='a b c d e...z'
var2='1 2 3 4 5...26'
var3='I II III IV V...XXVI'
zip_var1_var2 () {
set $var1
for v2 in $var2; do
echo "$1,$v2"
shift
done
}
zip_var12_var3 () {
set $(zip_var1_var2)
for v3 in $var3; do
echo "$1,$v3"
shift
done
}
for x in $(zip_var12_var3); do
echo "$x"
done
如果您愿意使用eval
并且确信这样做是安全的,您可以编写像
zip () {
if [ $# -eq 1 ]; then
eval echo $$1
return
fi
a1=$1
shift
x=$*
set $(eval echo $$a1)
for v in $(zip $x); do
printf '=== %sn' "$1,$v" >&2
echo "$1,$v"
shift
done
}
zip var1 var2 var3 # Note the arguments are the *names* of the variables to zip
如果您可以使用数组,那么(例如,在bash
中)
var1=(a b c d e)
var2=(1 2 3 4 5)
var3=(I II III IV V)
for i in "${!var1[@]}"; do
printf '%s,%s,%sn' "${var1[i]}" "${var2[i]}" "${var3[i]}"
done
使用下面的Perl一行代码:
perl -le '@in = map { [split] } @ARGV; for $i ( 0..$#{ $in[0] } ) { print join ",", map { $in[$_][$i] } 0..$#in; }' "$var1" "$var2" "$var3"
打印:
a,1,I
b,2,II
c,3,III
d,4,IV
e,5,V
z,26,XXVI
Perl单行程序使用这些命令行标志:-e
:告诉Perl查找内联代码,而不是在文件中查找。-l
:在执行内联代码之前去掉输入行分隔符(*NIX默认为"n"
),并在打印时附加。
输入变量必须像so"那样用双引号括起来,以防止空白分隔的单词被视为单独的参数。@ARGV
是一个命令行参数数组,这里是$var1
,$var2
,$var3
.@in
是一个包含3个元素的数组,每个元素都是对@ARGV
中对应元素在空格上分割后得到的数组的引用。注意,split
默认情况下使用空白分隔字符串,但您可以指定不同的分隔符,它接受正则表达式。
后续的for
循环打印以逗号分隔的@in
元素。
参见:perldoc perlrun
:如何执行Perl解释器:命令行开关perldoc perlvar
: Perl预定义变量
以下(几乎)是这个答案的副本,只是做了一些调整,使其适合这个问题。
原始问题
首先让我们分配一些变量,26的标记:
var1="$(echo {a..z})"
var2="$(echo {1..26})"
var3="$(echo I II III IV
V{,I,II,III} IX
X{,I,II,III} XIV
XV{,I,II,III} XIX
XX{,I,II,III} XXIV
XXV XXVI)"
var4="$(echo {A..Z})"
var5="$(echo {010101..262626..10101})"
现在我们需要一个"魔法"函数来压缩任意数量的变量,理想情况下是纯Bash:
zip_vars var1 # a trivial test
zip_vars var{1..2} # a slightly less trivial test
zip_vars var{1..3} # the original question
zip_vars var{1..4} # more vars, becasuse we can
zip_vars var{1..5} # more vars, because why not
zip_vars
看起来像什么?下面是一个纯Bash版本,没有任何外部命令:
zip_vars() {
local var
for var in "$@"; do
local -a "array_${var}"
local -n array_ref="array_${var}"
array_ref=(${!var})
local -ar "array_${var}"
done
local -n array_ref="array_${1}"
local -ir size="${#array_ref[@]}"
local -i i
local output
for ((i = 0; i < size; ++i)); do
output=
for var in "$@"; do
local -n array_ref="array_${var}"
output+=",${array_ref[i]}"
done
printf '%sn' "${output:1}"
done
}
工作原理:
- 将所有变量(通过引用(通过变量名)传递)拆分为数组。对于每个变量
varX
,它创建一个局部数组array_varX
。- 如果输入变量已经是Bash数组(见下文),实际上方式更容易,但是……我们坚持最初的问题。
- 它确定了第一个数组的
size
,然后盲目地期望所有数组都是该size
。 - 对于从
0
到size - 1
的每个索引i
,它将所有数组的i
个元素连接起来,以,
分隔。
数组让事情变得更容易
如果您从一开始就使用Bash数组,那么脚本将更短,看起来更简单,并且不会有任何字符串到数组的转换。
zip_arrays() {
local -n array_ref="$1"
local -ir size="${#array_ref[@]}"
local -i i
local output
for ((i = 0; i < size; ++i)); do
output=
for arr in "$@"; do
local -n array_ref="$arr"
output+=",${array_ref[i]}"
done
printf '%sn' "${output:1}"
done
}
arr1=({a..z})
arr2=({1..26})
arr3=( I II III IV
V{,I,II,III} IX
X{,I,II,III} XIV
XV{,I,II,III} XIX
XX{,I,II,III} XXIV
XXV
XXVI)
arr4=({A..Z})
arr5=({010101..262626..10101})
zip_arrays arr1 # a trivial test
zip_arrays arr{1..2} # a slightly less trivial test
zip_arrays arr{1..3} # (almost) the original question
zip_arrays arr{1..4} # more arrays, becasuse we can
zip_arrays arr{1..5} # more arrays, because why not