如何在 bash 中递归地将唯一标识符附加到一系列相同的字符串中?



我有一个名为test2的文件.txt其中包含以下内容:

string_9989 1.000 1.4567
string_9989 1.001001 1.2345
string_9989 1.1111111 2.22222222 3.33333333
string_9989 1.0000 1.4567 2.3456
string_1234 1.000000 1.3456
string_1234 1.3456 2.3456 3.5678
string_1234 1.234 3.456 5.678 6.789
string_1234 1.2 3.4 4.5 5.6

string_9989的第一个实例与string_1234的第一个实例相关;第二个string_9989实例与第二个string_1234实例相关,依此类推。我想提取字符串的每个实例的行并将它们打印到新文件中(因此,从 string_9989 和 string_1234 的第一个实例到 1 个文件,每个实例的第二个实例到另一个文件等(。我的想法是将索引附加到字符串的每个实例,并使用 grep 提取该实例并打印到新文件,但我无法成功创建索引。

我已经尝试了以下代码,其中我:

  • 将 string_[0-9] 的每个唯一实例保存到数组中
  • 使用 sed 遍历数组,将 _$j 添加到字符串末尾
#!/bin/bash
mapfile -t string_array < <( grep -Eio "string_[0-9]*" test2.txt | sort -u )
for i in ${string_array[@]}; do
count_path=$(grep -c $i test2.txt)
j=0
while [ $count_path -gt 0 ]; do
sed -i "$j,/$i/{s/<$i>/&_$j/}" test2.txt
let count_path=$(expr $count_path - 1)
let j=j+1
done
done

我期待这个输出:

string_9989_0 1.000 1.4567
string_9989_1 1.001001 1.2345
string_9989_2 1.1111111 2.22222222 3.33333333
string_9989_3 1.0000 1.4567 2.3456
string_1234_0 1.000000 1.3456
string_1234_1 1.3456 2.3456 3.5678
string_1234_2 1.234 3.456 5.678 6.789
string_1234_3 1.2 3.4 4.5 5.6

但相反,我得到了这个:

string_9989_0 1.000 1.4567
string_9989_1 1.001001 1.2345
string_9989_2 1.1111111 2.22222222 3.33333333
string_9989_3 1.0000 1.4567 2.3456
string_1234_0 1.000000 1.3456
string_1234 1.3456 2.3456 3.5678
string_1234 1.234 3.456 5.678 6.789
string_1234 1.2 3.4 4.5 5.6

为什么它没有完成第二个字符串的追加?我正在使用 bash 版本 4.1.2(1(

我想提取字符串的每个实例的行并将它们打印到新文件中(因此,从 string_9989 和 string_1234 的第一个实例到 1 个文件,每个文件的第二个实例到另一个文件的行,等等(

尴尬的救援:

awk '{ if (n != $1) { cnt=1; n=$1; }; print $0 > "file" cnt ".txt"; cnt=cnt+1; }'

以下脚本:

cat <<EOF |
string_9989 1.000 1.4567
string_9989 1.001001 1.2345
string_9989 1.1111111 2.22222222 3.33333333
string_9989 1.0000 1.4567 2.3456
string_1234 1.000000 1.3456
string_1234 1.3456 2.3456 3.5678
string_1234 1.234 3.456 5.678 6.789
string_1234 1.2 3.4 4.5 5.6
EOF
awk '{ if (n != $1) { cnt=1; n=$1; }; print $0 > "file" cnt ".txt"; cnt=cnt+1; }'
find
tail -n+1 *

将生成以下输出:

./file4.txt
./file1.txt
./file3.txt
./file2.txt
==> file1.txt <==
string_9989 1.000 1.4567
string_1234 1.000000 1.3456
==> file2.txt <==
string_9989 1.001001 1.2345
string_1234 1.3456 2.3456 3.5678
==> file3.txt <==
string_9989 1.1111111 2.22222222 3.33333333
string_1234 1.234 3.456 5.678 6.789
==> file4.txt <==
string_9989 1.0000 1.4567 2.3456
string_1234 1.2 3.4 4.5 5.6
  • 首先,我们检查实例是否与最后一行不同
  • 如果不同,则我们将cnt重置为1并记住当前实例
  • 然后我们printf $0整行打印成"file" cnt ".txt"文件名
  • 之后,我们增加计数。
  • 输入文件必须在第一列上排序。

模仿awk的bash解决方案看起来像:

while IFS=' ' read -r instance rest; do
if [ "${last_instance:-}" != "$instance" ]; then
cnt=1
last_instance=$instance
fi
printf "%s %sn" "$instance" "$rest" >> "file${cnt}.txt"
cnt=$((cnt + 1))
done

你依赖标准 unix 字符串处理工具(如 grep 和 sed(的直觉是一个很好的选择,如果你进一步考虑这个想法并添加粘贴和拆分,以及 <(进程替换(,这个任务可能非常简单:

a=`grep string_9989 test2.txt`
b=`grep string_1234 test2.txt`
both=`paste <(echo "$a") <(echo "$b")` # paste them side-by-side
echo "$both" | split -l1 # split into n 1-line files

这可能对你有用(GNU grep,粘贴和拆分(:

paste -d'n' <(grep 9989 file) <(grep 1234 file) | split -dl2 - file

使用两次 grep 调用将file一分为二,并使用粘贴交错文件。

将生成的文件通过管道拆分,生成的文件将命名为file00, file01, file02 etc

解决您的实际问题I want to extract the line for each instance of the strings and print them to new files

使用 GNU awk:

awk '{print > "out" ++cnt[$1]}' file

对于任何awk,并假设您的真实输入像您的样本一样排序:

awk '$1!=prev{prev=$1; close(out); out="out" ++cnt} {print > out}' file

最新更新