在bash中循环使用多对值

时间:2015-02-25 17:12:54

标签: bash for-loop nested-loops paste

我有10个文本文件,我希望paste每个文件都有一对,这样我就有5个文件。

我尝试了以下内容:

for i in 4_1 5_1 6_1 7_1 8_1
do
for j in 4_2 5_2 6_2 7_2 8_2
do
paste ${i}.txt ${j}.txt > ${i}.${j}.txt
done
done

但是,此代码组合了所有可能的组合,而不是仅仅组合匹配对。

所以我希望文件4_1.txt4_2.txt5_1.txt5_2.txt等配对。

7 个答案:

答案 0 :(得分:22)

我同意fedorqui目前在问题背景下提出的答案。以下仅提供一些更一般的答案。

另一种通用方法(对于bash 4.0或更新版本)是将对存储在关联数组中:

declare -A pairs=( [4_1]=4_2 [5_1]=5_2 [6_1]=6_2 [7_1]=7_2 [8_1]=8_2 )
for i in "${!pairs[@]}"; do
  j=${pairs[$i]}
  paste "$i.txt" "$j.txt" >"${i}.${j}.txt"
done

另一个(与旧版本的bash兼容)是使用多个传统阵列:

is=( 4_1 5_1 6_1 7_1 8_1 )
js=( 4_2 5_2 6_2 7_2 8_2 )
for idx in "${!is[@]}"; do
  i=${is[$idx]}
  j=${js[$idx]}
  paste "$i.txt" "$j.txt" >"$i.$j.txt"
done

答案 1 :(得分:9)

如果你想使用一个变量并使用它执行和操作,你只需要使用一个循环:

for file in 4 5 6 7 8
do
   paste "${file}_1" "${file}_2"
done

这样做

paste 4_1 4_2
paste 5_1 5_2
...

答案 2 :(得分:6)

您可以使用关联数组:

animals=(dog cat mouse)
declare -A size=(
  [dog]=big
  [cat]=medium
  [mouse]=small
)
declare -A sound=(
  [dog]=barks
  [cat]=purrs
  [mouse]=cheeps
)
for animal in "${animals[@]}"; do
  echo "$animal is ${size[$animal]} and it ${sound[$animal]}"
done

这允许你遍历对,三元组等。积分:最初的想法来自@ CharlesDuffy的答案。

答案 3 :(得分:5)

到目前为止最简单:

for i in "1 a" "2 b" "3 c"; do a=( $i ); echo ${a[1]}; echo ${a[0]}; done

a
1
b
2
c
3

答案 4 :(得分:4)

有一种常见的模式,你有一对文件,其中一对名称可以很容易地从另一个名称派生。如果您知道文件的名称是X而另一个文件是Y,则您有以下常见用例。

  • 对于重命名,Y为X,删除了扩展名和/或添加了日期戳。
  • 对于转码,Y是具有不同扩展名的X,也许是不同的目录。
  • 对于许多数据分析任务,X和Y共享文件名的某些部分,但具有不同的参数或扩展名。

所有这些都适用于相同的粗糙代码骨架。

for x in path/to/base*.ext; do
    dir=${x%/*}   # Trim trailing file name, keep dir
    base=${x##*/} # Trim any leading directory

    # In this case, $y has a different subdirectory and a different extension
    y=${dir%/to}/from/${base%.ext}.newext

    # Maybe check if y exists?  Or doesn't exist?
    if [ -e "$y" ]; then
        echo "$0: $y already exists -- skipping" >&2
        continue
    fi

    mv or ffmpeg or awk or whatever "$x" and "$y"
done

这里的关键是观察y可以从x得到一些简单的变量替换。因此,您循环遍历x值,并在循环内找出相应的y值。

在这里,我们使用了shell的内置${variable#prefix}${variable%suffix}运算符来分别返回变量的值,其中包含任何前导prefix或尾随suffix。 。 (还有##%%来匹配最长的,而不是最短的可能匹配。#%之后的表达式是常规的shell glob模式。)这些通常应该是你所需要的,尽管你经常看到sedawk脚本,即使是这个琐碎的工作(你通常应该尽量避免外部过程),当然还有更多要求转型。

如果你需要遍历散布在不同目录中的x个文件,那么循环应该从类似

开始
 find dir1 dir2 etc/and/so/forth -type f -name 'x-files*.ext' -print |
 while IFS='' read -r x; do
     :

类似问题中常见的问题是无法正确引用$x$y的答案。通常,包含文件名的任何变量应始终使用双引号。

在X和Y不相关的情况下,一个常见的解决方案是遍历包含映射的here文档:

while read -r x y; do
    : stuff with "$x" and "$y"
done <<'____HERE'
    first_x_value  first_y_value
    another_x      corresponding_y
    random         surprise
____HERE

答案 5 :(得分:1)

以上内容对我不起作用,但以下内容确实从有序列表中成对读取值

(可以添加成对的“ read-lines”多于一对:-)

while read x; do
  read y
  echo $x $y
done << '___HERE'
X1
Y1
X2
Y2
X3
Y3
___HERE

产生

X1 Y1
X2 Y2
X3 Y3

答案 6 :(得分:0)

使用read -r x yread x && read y的区别

while read -r x y; do
    echo "$x and $y"
done <<'____HERE'
    A B
    C D
____HERE

将打印

A and B
C and D

while read x && read y; do
    echo "$x and $y"
done <<'____HERE'
    A B
    C D
____HERE

将打印

A B and C D

第一个示例在space上拆分,而第二个示例在newline上拆分。