循环遍历 bash 中的值对

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28725333/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 12:26:00  来源:igfitidea点击:

Looping over pairs of values in bash

bashfor-loopnested-loopspaste

提问by Rish

I have 10 text files and I want to pasteeach file with its pair, such that I have 5 total files.

我有 10 个文本文件,我想要paste每个文件及其对,这样我总共有 5 个文件。

I tried the following:

我尝试了以下方法:

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

However, this code combines every possible combination instead of just combining the matching pairs.

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

So I would like file 4_1.txtto be paired with 4_2.txt, 5_1.txtwith 5_2.txt, etc.

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

采纳答案by fedorqui 'SO stop harming'

If you want to use one variable and perform and action with it, you just need to use one loop:

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

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

This will do

这会做

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

回答by Charles Duffy

I agree with the answer currently proposed by fedorqui in the context of the question currently asked. The below is given only to provide some more general answers.

我同意 fedorqui 在当前提出的问题的背景下目前提出的答案。下面给出的只是为了提供一些更一般的答案。

One more general approach (for bash 4.0 or newer) is to store your pairs in an associative array:

一种更通用的方法(对于 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

Another (compatible with older releases of bash) is to use more than one conventional array:

另一种(与较旧版本的 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

回答by VasiliNovikov

You can use an associative array:

您可以使用关联数组:

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

This allows you traversing pairs, triples, etc. Credits: the original idea is taken from @CharlesDuffy-s answer.

这允许您遍历对、三元组等。 致谢:最初的想法来自@CharlesDuffy-s 的答案。

回答by tripleee

There is a common pattern where you have pairs of files, where one name of the pair can be easily derived from the other. If the file you know the name of is X and the other file is Y, you have the following common use cases.

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

  • For renaming, Y is X with an extension removed and/or a date stamp added.
  • For transcoding, Y is X with a different extension and perhaps a different directory.
  • For many data analysis tasks, X and Y share some parts of the file name, but have different parameters or extensions.
  • 对于重命名,Y 是 X,删除了扩展名和/或添加了日期戳。
  • 对于转码,Y 是具有不同扩展名和可能不同目录的 X。
  • 对于许多数据分析任务,X 和 Y 共享文件名的某些部分,但具有不同的参数或扩展名。

All of these lend themselves to the same rough code skeleton.

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

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 "
 find dir1 dir2 etc/and/so/forth -type f -name 'x-files*.ext' -print |
 while IFS='' read -r x; do
     :
: $y already exists -- skipping" >&2 continue fi mv or ffmpeg or awk or whatever "$x" and "$y" done

The key here is the observation that ycan be derived from xwith some simple variable substitutions. So you loop over the xvalues, and figure out the corresponding yvalue inside the loop.

这里的关键是y可以通过x一些简单的变量替换得出的观察结果。所以你遍历这些x值,并找出y循环内的相应值。

Here, we have used the shell's built-in ${variable#prefix}and ${variable%suffix}operators to return the variable's value with any leading prefixor trailing suffix, respectively, trimmed off. (There is also ##and %%to match the longest, instead of the shortest, possible match. The expression after #or %is a regular shell glob pattern.) These should usually be all you need, although you frequently see sedor awkscripts even for this trivial job (where really you should usually try to avoid an external process), as well as of course for more demanding transformations.

在这里,我们使用了 shell 的内置${variable#prefix}${variable%suffix}运算符来返回变量的值,分别删除了任何前导prefix或尾随suffix。(也有##%%匹配最长,而不是最短的,可能比赛。后面的表达式#%一个普通的shell glob模式。)这些平时应该是你所需要的,虽然你经常看到sedawk脚本,即使是这样一个简单的工作(您通常应该尽量避免外部流程),当然也应该避免要求更高的转换。

If you need to loop over xfiles scattered across different directories, maybe the loop should start with something like

如果您需要遍历x分散在不同目录中的文件,也许循环应该以类似的方式开始

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

A commonly seen problem in similar questions is answers which fail to quote $xand $ycorrectly. Generally, any variable containing a file name should always be in double quotes.

类似问题中一个常见的问题是无法正确引用$x和引用的答案$y。通常,任何包含文件名的变量都应该用双引号引起来。

Where X and Y are unrelated, a common solution is to loop over a here document containing the mapping:

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

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

回答by splaisan

the above did not work for me, but the following does read values in pairs from an ordered list

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

(can be more than pairs adding extra 'read-lines' :-)

(可以不仅仅是成对添加额外的“阅读行”:-)

X1 Y1
X2 Y2
X3 Y3

produces

产生

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

回答by Vivek

There is difference between using read -r x yand read x && read y

使用read -r x y和之间有区别read x && read y

A and B
C and D

will print

将打印

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

whereas,

然而,

A B and C D

will print

将打印

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

First example splits on space, while second splits on newline.

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

回答by Fibo Kowalsky

Simplest so far:

迄今为止最简单的:

##代码##