循环 bash 中的值对
Looping over pairs of values in bash
我有 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
配对,等等
如果你想使用一个变量并用它执行和操作,你只需要使用一个循环:
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
...
我同意 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
有一种常见的模式,即您有成对的文件,其中一个名称可以很容易地从另一个名称派生出来。如果您知道名称的文件是 X,而另一个文件是 Y,则您有以下常见用例。
- 对于重命名,Y 是 X,删除了扩展名and/or 添加了日期戳。
- 对于转码,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 "[=10=]: $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}
运算符来 return 变量的值与任何前导 prefix
或尾随 suffix
,分别被修剪掉。 (还有 ##
和 %%
来匹配最长的,而不是最短的可能匹配。 #
或 %
之后的表达式是常规 shell glob 模式。)这些通常应该是你所需要的,尽管你经常看到 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
您可以使用关联数组:
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 答案。
以上对我不起作用,但以下确实从有序列表中成对读取值
(可以多对添加额外的 '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
到目前为止最简单的:
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
我有 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
配对,等等
如果你想使用一个变量并用它执行和操作,你只需要使用一个循环:
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
...
我同意 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
有一种常见的模式,即您有成对的文件,其中一个名称可以很容易地从另一个名称派生出来。如果您知道名称的文件是 X,而另一个文件是 Y,则您有以下常见用例。
- 对于重命名,Y 是 X,删除了扩展名and/or 添加了日期戳。
- 对于转码,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 "[=10=]: $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}
运算符来 return 变量的值与任何前导 prefix
或尾随 suffix
,分别被修剪掉。 (还有 ##
和 %%
来匹配最长的,而不是最短的可能匹配。 #
或 %
之后的表达式是常规 shell glob 模式。)这些通常应该是你所需要的,尽管你经常看到 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
您可以使用关联数组:
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 答案。
以上对我不起作用,但以下确实从有序列表中成对读取值
(可以多对添加额外的 '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
到目前为止最简单的:
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