循环遍历 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
Looping over pairs of values in bash
提问by Rish
I have 10 text files and I want to paste
each 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.txt
to be paired with 4_2.txt
, 5_1.txt
with 5_2.txt
, etc.
所以我希望文件4_1.txt
与4_2.txt
、5_1.txt
与5_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 y
can be derived from x
with some simple variable substitutions. So you loop over the x
values, and figure out the corresponding y
value 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 prefix
or 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 sed
or awk
scripts 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模式。)这些平时应该是你所需要的,虽然你经常看到sed
或awk
脚本,即使是这样一个简单的工作(您通常应该尽量避免外部流程),当然也应该避免要求更高的转换。
If you need to loop over x
files 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 $x
and $y
correctly. 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 y
and 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:
迄今为止最简单的:
##代码##