在 bash 中循环值对

问题描述 投票:0回答:6

我有 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.txt
4_2.txt
配对,
5_1.txt
5_2.txt
配对,等等

bash for-loop nested-loops paste
6个回答
43
投票

我同意 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

32
投票

迄今为止最简单的:

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

14
投票

您可以使用关联数组:

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-s 的回答。


11
投票

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

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
...

7
投票

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

(可以多对添加额外的“阅读行”:-)

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

产生

X1 Y1
X2 Y2
X3 Y3

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 全局模式。)这些通常应该是您所需要的,尽管您经常看到
sed
awk
脚本,即使是对于这项琐碎的工作(实际上您通常应该尝试避免外部过程),当然还有更苛刻的转换。

如果您需要循环遍历分散在不同目录中的

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 不相关时,常见的解决方案是循环遍历包含映射的此处文档:

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
© www.soinside.com 2019 - 2024. All rights reserved.