Awk 根据字符串的出现情况对列重新编号
很抱歉,我对linux还比较陌生 我有这样一个文件:Awk 根据字符串的出现情况对列重新编号,awk,sed,seq,Awk,Sed,Seq,很抱歉,我对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
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 $2}' temp1.txt
awk -i '{print NR $2}' temp2.txt
awk -i '{print NR $2}' temp3.txt
cat temp1.txt >> File.txt
cat temp2.txt >> File.txt
cat temp3.txt >> File.txt
但是我很确定我的语法很糟糕,因为我只熟悉sed而不熟悉awk
非常感谢您的帮助。编辑:这是一个使用GNUawk
的解决方案,它保留了实际的空间。如果您的split
支持4个参数。阅读手册后,我得到了它,即使我很高兴我找到了它,这将是有益的
awk '
{
n=split($0,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
字段数。如果
省略r,而使用FS。首先清除阵列a和SEP。seps[i]是一个领域
由r匹配的分隔符
a[i]和a[i+1]。如果r是一个空格,那么s中的前导空格进入额外的数组元素
seps[0]和尾随的白色-
空格进入额外的数组元素seps[n],其中n是split(s,a,r,seps)的返回值。
分裂行为相同
要进行字段拆分,如上所述
第一种解决方案:请尝试以下方法
awk '{$2=sprintf("%s%02d",$2,++a[$2])} 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 '{$2=$4=sprintf("%s%02d",$2,++a[$2])} 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
awk '{$(NF+1)=sprintf("%s%02d",$2,++a[$2])} 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 '{$2=$4=sprintf("%s%02d",$2,++a[$2])} 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
awk '{$(NF+1)=sprintf("%s%02d",$2,++a[$2])} 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 '{$2=sprintf("%s%02d",$2,++a[$2]);}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[$2]); sub($2" ",$2r)}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
请注意,这假设第一个字段的值与第二个字段的值不重叠,如图所示,否则您需要进行保护,以仅保留对第二个字段的更改。对于第二个字段,可以很容易地通过在匹配和替换值前加上单个空格来完成 第三个参数使用GNU awk进行匹配()和
\S/\S
的缩写[^[:space]:]/[:space:]
:
$ awk 'match($0,/(\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
即使前面的字段与目标字段具有相同的值,或者目标字段包含RE元字符或任何其他内容,上述操作仍适用于所有输入
以上内容用于修改第二个字段。通常,要修改n=4的第n个字段,例如,硬编码将是:
$ awk 'match($0,/((\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($0,"((\\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",$1,$2,++$kv{$2},$3)/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",$1,$2,++$kv{$2})/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",$1,++$kv{$1})/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",$1,++$kv{$1})/e ' wagner.txt
解释:
$ awk 'BEGIN {
FS=OFS="" # empty field separators
}
{
$6="" # null $6
$7=((b=++a[$5])>9?"":0) b # $7 carries the count, with leading 0 if below 10
}1' file
这太棒了!非常感谢。现在,如果我需要将该列更改为第4列,我只需将$2的占用率替换为$4?@WagnerAG,你的意思是2和4应该具有相同的值?@WagnerAG,请检查我的编辑解决方案,并让我知道这些是否有帮助。非常感谢你的帮助@RavinderSingh13我编辑了上面的帖子,以反映我的想法mean@WagnerAG,请检查我的编辑解决方案,现在让我知道?这是一个!非常感谢。然而,我有一个问题;在我使用的实际文件中,有一些列具有不同的间距,使用这些列后,格式会发生变化。因此,如果上面的文件还有5个空格的话,它现在变成了单空格。非常感谢!这为我节省了大量时间。@WagnerAG希望你能用节省的时间做些有用的事情:)。。。。例如阅读Arnold Robbins的第四版《有效的AWK编程》:-)@karakfa如果我这样做,那么%02d格式将导致
c01
在C和01OK之间有一个空格,您必须用数字替换两个空格<代码>sprintf需要相应更改。是。。我想这里不需要3美元。。让我检查一下并更新一下,我们又找到了一种方法[^^]\S+
或$6=int(++a[$5]/10)$7=a[$5]%10