如何迭代多个变量并使用Shell脚本回显它们?



考虑以下动态变量,它们每次都可能发生变化。有时甚至可能有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&quot那样用双引号括起来,以防止空白分隔的单词被视为单独的参数。
@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
}

工作原理:

  1. 将所有变量(通过引用(通过变量名)传递)拆分为数组。对于每个变量varX,它创建一个局部数组array_varX
    • 如果输入变量已经是Bash数组(见下文),实际上方式更容易,但是……我们坚持最初的问题。
  2. 它确定了第一个数组的size,然后盲目地期望所有数组都是该size
  3. 对于从0size - 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

相关内容

  • 没有找到相关文章

最新更新