如何迭代多个变量并使用 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
}

工作原理:

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