根据字符串的出现对列重新编号
Renumbering a column based on occurrence of a string
对 linux 很陌生,抱歉。
我有这样一个文件:
1 C foo C bar
2 C foo C bar
3 C foo C bar
4 H foo H bar
5 H foo H bar
6 O foo O bar
我需要让它成为:
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
**很遗憾,foo 和 C 之间的间距以及 C 和 bar 之间的间距必须保持不变。
我以分段方式进行了尝试,我提取了包含不同标识符 C、H 和 O 的行,将它们放在一个临时文件中。然后我尝试按顺序排列它们,然后将原始文件拼接回去。
#!/bin/bash
sed -i -e "/ C /w temp1.txt" -e "//d" File.txt
sed -i -e "/ H /w temp2.txt" -e "//d" File.txt
sed -i -e "/ O /w temp3.txt" -e "//d" File.txt
`awk -i '{print NR }' temp1.txt
awk -i '{print NR }' temp2.txt
awk -i '{print NR }' temp3.txt
cat temp1.txt >> File.txt
cat temp2.txt >> File.txt
cat temp3.txt >> File.txt
但是我很确定我的语法很糟糕,因为我真的只熟悉 sed 而不是 awk。
如有任何帮助,我们将不胜感激,谢谢。
编辑: 这是 GNU awk
的解决方案,它保留了实际空间。如果你的 split
支持 4 个参数。阅读手册页后我明白了,即使我很高兴找到它,它也会有所帮助。
awk '
{
n=split([=10=],array," ",b)
array[2]=sprintf("%s%02d",array[2],++a[array[2]])
line=b[0]
for(i=1;i<=n;i++){
line=(line array[i] b[i])
}
print line
}' Input_file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
GNU awk
手册页中的关于 split
4 个参数:
split(s, a [, r [, seps] ])
Split the string s into the array a and the separators array seps on the regular expression r, and return the
number of fields. If
r is omitted, FS is used instead. The arrays a and seps are cleared first. seps[i] is the field
separator matched by r between
a[i] and a[i+1]. If r is a single space, then leading whitespace in s goes into the extra array element
seps[0] and trailing white-
space goes into the extra array element seps[n], where n is the return value of split(s, a, r, seps).
Splitting behaves identically
to field splitting, described above.
第一个解决方案:能否请您尝试以下,
awk '{=sprintf("%s%02d",,++a[])} 1' Input_file
输出如下。
1 C01 bar C
2 C02 bar C
3 C03 bar C
4 H01 bar H
5 H02 bar H
6 O01 bar O
第二个解决方案:如果你想在 $2 和 $4 两个地方都有值,那么请执行以下操作。
awk '{==sprintf("%s%02d",,++a[])} 1' Input_file
1 C01 bar C01
2 C02 bar C02
3 C03 bar C03
4 H01 bar H01
5 H02 bar H02
6 O01 bar O01
第三个解决方案: 如果您想 add/insert 在行的最后添加一个新列,请执行以下操作。
awk '{$(NF+1)=sprintf("%s%02d",,++a[])} 1' Input_file
1 C bar C C01
2 C bar C C02
3 C bar C C03
4 H bar H H01
5 H bar H H02
6 O bar O O01
使用简单的 awk 脚本:
$ awk '{=sprintf("%s%02d",,++a[]);}1' file
1 C01 foo C
2 C02 foo C
3 C03 foo C
4 H01 foo H
5 H02 foo H
6 O01 foo O
保留初始字段位置的相同解决方案
$ awk '{r=sprintf("%02d",++a[]); sub(" ",r)}1' file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
请注意,这假定第一个字段值不与第二个字段值重叠,如图所示,否则您需要注意只对第二个字段进行更改。对于第二个字段,可以通过在匹配和替换值前面加上单个 space 来轻松完成。
GNU awk 将第 3 个参数设为 match()
,\S/\s
shorthand 设为 [^[:space]:]]/[[:space:]]
:
$ awk 'match([=10=],/(\S+\s+)(\S+)(.*)/,a){ printf "%s%s%02d%s\n", a[1], a[2], ++cnt[a[2]], a[3] }' file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
以上将适用于 ALL 输入,即使前面的字段与目标字段具有相同的值,或者如果目标字段包含 RE 元字符或其他任何内容。
以上是修改第2个字段。一般来说,修改 n=4 的第 n 个字段,例如,硬编码将是:
$ awk 'match([=11=],/((\S+\s+){3})(\S+)(.*)/,a){ printf "%s%s%02d%s\n", a[1], a[3], ++cnt[a[3]], a[4]}' file
1 C foo C01 bar
2 C foo C02 bar
3 C foo C03 bar
4 H foo H01 bar
5 H foo H02 bar
6 O foo O01 bar
如果它作为参数传递而不是硬编码:
$ awk -v n=4 'match([=12=],"((\S+\s+){"n-1"})(\S+)(.*)",a){ printf "%s%s%02d%s\n", a[1], a[3], ++cnt[a[3]], a[4]}' file
1 C foo C01 bar
2 C foo C02 bar
3 C foo C03 bar
4 H foo H01 bar
5 H foo H02 bar
6 O foo O01 bar
虽然没有标记 Perl,但它似乎很适合这些情况。如果您正在考虑使用 Perl,请查看此内容。
> cat wagner.txt
1 C foo C bar
2 C foo C bar
3 C foo C bar
4 H foo H bar
5 H foo H bar
6 O foo O bar
> perl -pe 's/(\s+)(\S+)(\s+)/sprintf("%s%s%02d%s",,,++$kv{},)/e ' wagner.txt
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
>
感谢 Karakfa,通过删除 $3
可以进一步缩短答案
> perl -pe 's/(\s+)(\S+)/sprintf("%s%s%02d",,,++$kv{})/e ' wagner.txt
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
>
进一步删除一组的另一种方法
> perl -pe 's/([^^]\S+)/sprintf("%s%02d",,++$kv{})/e ' wagner.txt
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
>
或使用环视
perl -pe 's/([^?!]\S+)/sprintf("%s%02d",,++$kv{})/e ' wagner.txt
$ awk 'BEGIN{FS=OFS=""}{="";=((b=++a[])>9?"":0) b}1' file file file file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
...
6 O03 foo O bar
1 C10 foo C bar
2 C11 foo C bar
解释:
$ awk 'BEGIN {
FS=OFS="" # empty field separators
}
{
="" # null
=((b=++a[])>9?"":0) b # carries the count, with leading 0 if below 10
}1' file
对 linux 很陌生,抱歉。
我有这样一个文件:
1 C foo C bar
2 C foo C bar
3 C foo C bar
4 H foo H bar
5 H foo H bar
6 O foo O bar
我需要让它成为:
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
**很遗憾,foo 和 C 之间的间距以及 C 和 bar 之间的间距必须保持不变。
我以分段方式进行了尝试,我提取了包含不同标识符 C、H 和 O 的行,将它们放在一个临时文件中。然后我尝试按顺序排列它们,然后将原始文件拼接回去。
#!/bin/bash
sed -i -e "/ C /w temp1.txt" -e "//d" File.txt
sed -i -e "/ H /w temp2.txt" -e "//d" File.txt
sed -i -e "/ O /w temp3.txt" -e "//d" File.txt
`awk -i '{print NR }' temp1.txt
awk -i '{print NR }' temp2.txt
awk -i '{print NR }' temp3.txt
cat temp1.txt >> File.txt
cat temp2.txt >> File.txt
cat temp3.txt >> File.txt
但是我很确定我的语法很糟糕,因为我真的只熟悉 sed 而不是 awk。
如有任何帮助,我们将不胜感激,谢谢。
编辑: 这是 GNU awk
的解决方案,它保留了实际空间。如果你的 split
支持 4 个参数。阅读手册页后我明白了,即使我很高兴找到它,它也会有所帮助。
awk '
{
n=split([=10=],array," ",b)
array[2]=sprintf("%s%02d",array[2],++a[array[2]])
line=b[0]
for(i=1;i<=n;i++){
line=(line array[i] b[i])
}
print line
}' Input_file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
GNU awk
手册页中的关于 split
4 个参数:
split(s, a [, r [, seps] ]) Split the string s into the array a and the separators array seps on the regular expression r, and return the
number of fields. If r is omitted, FS is used instead. The arrays a and seps are cleared first. seps[i] is the field separator matched by r between a[i] and a[i+1]. If r is a single space, then leading whitespace in s goes into the extra array element seps[0] and trailing white- space goes into the extra array element seps[n], where n is the return value of split(s, a, r, seps). Splitting behaves identically to field splitting, described above.
第一个解决方案:能否请您尝试以下,
awk '{=sprintf("%s%02d",,++a[])} 1' Input_file
输出如下。
1 C01 bar C
2 C02 bar C
3 C03 bar C
4 H01 bar H
5 H02 bar H
6 O01 bar O
第二个解决方案:如果你想在 $2 和 $4 两个地方都有值,那么请执行以下操作。
awk '{==sprintf("%s%02d",,++a[])} 1' Input_file
1 C01 bar C01
2 C02 bar C02
3 C03 bar C03
4 H01 bar H01
5 H02 bar H02
6 O01 bar O01
第三个解决方案: 如果您想 add/insert 在行的最后添加一个新列,请执行以下操作。
awk '{$(NF+1)=sprintf("%s%02d",,++a[])} 1' Input_file
1 C bar C C01
2 C bar C C02
3 C bar C C03
4 H bar H H01
5 H bar H H02
6 O bar O O01
使用简单的 awk 脚本:
$ awk '{=sprintf("%s%02d",,++a[]);}1' file
1 C01 foo C
2 C02 foo C
3 C03 foo C
4 H01 foo H
5 H02 foo H
6 O01 foo O
保留初始字段位置的相同解决方案
$ awk '{r=sprintf("%02d",++a[]); sub(" ",r)}1' file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
请注意,这假定第一个字段值不与第二个字段值重叠,如图所示,否则您需要注意只对第二个字段进行更改。对于第二个字段,可以通过在匹配和替换值前面加上单个 space 来轻松完成。
GNU awk 将第 3 个参数设为 match()
,\S/\s
shorthand 设为 [^[:space]:]]/[[:space:]]
:
$ awk 'match([=10=],/(\S+\s+)(\S+)(.*)/,a){ printf "%s%s%02d%s\n", a[1], a[2], ++cnt[a[2]], a[3] }' file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
以上将适用于 ALL 输入,即使前面的字段与目标字段具有相同的值,或者如果目标字段包含 RE 元字符或其他任何内容。
以上是修改第2个字段。一般来说,修改 n=4 的第 n 个字段,例如,硬编码将是:
$ awk 'match([=11=],/((\S+\s+){3})(\S+)(.*)/,a){ printf "%s%s%02d%s\n", a[1], a[3], ++cnt[a[3]], a[4]}' file
1 C foo C01 bar
2 C foo C02 bar
3 C foo C03 bar
4 H foo H01 bar
5 H foo H02 bar
6 O foo O01 bar
如果它作为参数传递而不是硬编码:
$ awk -v n=4 'match([=12=],"((\S+\s+){"n-1"})(\S+)(.*)",a){ printf "%s%s%02d%s\n", a[1], a[3], ++cnt[a[3]], a[4]}' file
1 C foo C01 bar
2 C foo C02 bar
3 C foo C03 bar
4 H foo H01 bar
5 H foo H02 bar
6 O foo O01 bar
虽然没有标记 Perl,但它似乎很适合这些情况。如果您正在考虑使用 Perl,请查看此内容。
> cat wagner.txt
1 C foo C bar
2 C foo C bar
3 C foo C bar
4 H foo H bar
5 H foo H bar
6 O foo O bar
> perl -pe 's/(\s+)(\S+)(\s+)/sprintf("%s%s%02d%s",,,++$kv{},)/e ' wagner.txt
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
>
感谢 Karakfa,通过删除 $3
可以进一步缩短答案> perl -pe 's/(\s+)(\S+)/sprintf("%s%s%02d",,,++$kv{})/e ' wagner.txt
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
>
进一步删除一组的另一种方法
> perl -pe 's/([^^]\S+)/sprintf("%s%02d",,++$kv{})/e ' wagner.txt
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
5 H02 foo H bar
6 O01 foo O bar
>
或使用环视
perl -pe 's/([^?!]\S+)/sprintf("%s%02d",,++$kv{})/e ' wagner.txt
$ awk 'BEGIN{FS=OFS=""}{="";=((b=++a[])>9?"":0) b}1' file file file file
1 C01 foo C bar
2 C02 foo C bar
3 C03 foo C bar
4 H01 foo H bar
...
6 O03 foo O bar
1 C10 foo C bar
2 C11 foo C bar
解释:
$ awk 'BEGIN {
FS=OFS="" # empty field separators
}
{
="" # null
=((b=++a[])>9?"":0) b # carries the count, with leading 0 if below 10
}1' file