如何迭代多个变量并使用 Shell 脚本回显它们?
How to iterate over multiple variables and echo them using Shell Script?
考虑以下变量,它们是动态的并且每次都可能发生变化。有时甚至可能有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 循环和移位使这两个变量工作,使用下面 link
https://unix.stackexchange.com/questions/390283/how-to-iterate-two-variables-in-a-sh-script
您需要一次“压缩”两个变量。
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 ",$v2"
shift
done
}
zip_var12_var3 () {
set $(zip_var1_var2)
for v3 in $var3; do
echo ",$v3"
shift
done
}
for x in $(zip_var12_var3); do
echo "$x"
done
如果你愿意使用eval
并且确定这样做是安全的,你可以写一个像
这样的函数
zip () {
if [ $# -eq 1 ]; then
eval echo $
return
fi
a1=
shift
x=$*
set $(eval echo $$a1)
for v in $(zip $x); do
printf '=== %s\n' ",$v" >&2
echo ",$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,%s\n' "${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"
),并在打印时附加它。
输入变量必须用双引号“like so”,以防止空格分隔的单词被视为单独的参数。
@ARGV
是命令行参数的数组,这里是$var1
、$var2
、$var3
.
@in
是一个包含 3 个元素的数组,每个元素都是对一个数组的引用,该数组是通过在空白处拆分 @ARGV
的相应元素而获得的。请注意,split
默认情况下将字符串拆分为空格,但您可以指定不同的分隔符,它接受正则表达式。
随后的 for
循环打印 @in
个以逗号分隔的元素。
另请参见:
perldoc perlrun
: how to execute the Perl interpreter: command line switches
perldoc perlvar
: Perl predefined variables
和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
添加其他参数替换是不可扩展的。
以下是(几乎)一个副本,经过一些调整使其适合这个问题。
原始问题
首先让我们分配几个变量来玩,每个变量有 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_"
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 '%s\n' "${output:1}"
done
}
工作原理:
- 它将所有变量(通过引用(通过变量名)传递)拆分为数组。它为每个变量
varX
创建一个本地数组 array_varX
。
- 如果输入变量已经是 Bash 数组开始(见下文),实际上 会更容易 ,但是......我们最初坚持原来的问题.
- 它确定第一个数组的
size
,然后盲目地期望所有数组都是 size
。
- 对于从
0
到 size - 1
的每个索引 i
,它连接所有数组的第 i
个元素,由 ,
分隔。
数组让事情变得更简单
如果您从一开始就使用 Bash 数组,脚本会更短,看起来更简单,并且不会有任何字符串到数组的转换。
zip_arrays() {
local -n array_ref=""
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 '%s\n' "${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
考虑以下变量,它们是动态的并且每次都可能发生变化。有时甚至可能有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 循环和移位使这两个变量工作,使用下面 link https://unix.stackexchange.com/questions/390283/how-to-iterate-two-variables-in-a-sh-script
您需要一次“压缩”两个变量。
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 ",$v2"
shift
done
}
zip_var12_var3 () {
set $(zip_var1_var2)
for v3 in $var3; do
echo ",$v3"
shift
done
}
for x in $(zip_var12_var3); do
echo "$x"
done
如果你愿意使用eval
并且确定这样做是安全的,你可以写一个像
zip () {
if [ $# -eq 1 ]; then
eval echo $
return
fi
a1=
shift
x=$*
set $(eval echo $$a1)
for v in $(zip $x); do
printf '=== %s\n' ",$v" >&2
echo ",$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,%s\n' "${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"
),并在打印时附加它。
输入变量必须用双引号“like so”,以防止空格分隔的单词被视为单独的参数。
@ARGV
是命令行参数的数组,这里是$var1
、$var2
、$var3
.
@in
是一个包含 3 个元素的数组,每个元素都是对一个数组的引用,该数组是通过在空白处拆分 @ARGV
的相应元素而获得的。请注意,split
默认情况下将字符串拆分为空格,但您可以指定不同的分隔符,它接受正则表达式。
随后的 for
循环打印 @in
个以逗号分隔的元素。
另请参见:
perldoc perlrun
: how to execute the Perl interpreter: command line switches
perldoc perlvar
: Perl predefined variables
和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
添加其他参数替换是不可扩展的。
以下是(几乎)一个副本
原始问题
首先让我们分配几个变量来玩,每个变量有 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_"
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 '%s\n' "${output:1}"
done
}
工作原理:
- 它将所有变量(通过引用(通过变量名)传递)拆分为数组。它为每个变量
varX
创建一个本地数组array_varX
。- 如果输入变量已经是 Bash 数组开始(见下文),实际上 会更容易 ,但是......我们最初坚持原来的问题.
- 它确定第一个数组的
size
,然后盲目地期望所有数组都是size
。 - 对于从
0
到size - 1
的每个索引i
,它连接所有数组的第i
个元素,由,
分隔。
数组让事情变得更简单
如果您从一开始就使用 Bash 数组,脚本会更短,看起来更简单,并且不会有任何字符串到数组的转换。
zip_arrays() {
local -n array_ref=""
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 '%s\n' "${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