我有一个带有|
分隔符的文件,大约有 8k 条记录。
如果第 3 列为空,我想将其替换为第 2 列中的值。我们如何实现这一目标?
输入:
1|100437251|
2|51414204|
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
输出:
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
我尝试过使用sed
命令来替换sed -i "s/ /$2/g" input > output
.
答案1
和sed:
sed -E 's/^([^|]*\|)([^|]*)\|$/\1\2|\2/' infile
这部分([^|]*\|)
捕获第一列以及分隔符(\1
是反向引用)。
这部分([^|]*)
捕获第二列(\2
是反向引用)。
这部分\|$
捕获最后一个分隔符,即行结束符,后面没有任何其他分隔符(意味着第三列为空)。
此部分\1\2|\2
使用反向引用地址返回第一列和第二列,然后使用分隔符并再次复制第二列。
如果第三列不为空,但也可能包含空格字符,例如制表符/空格 ( [[:space:]]
),请改用此列。
sed -E 's/^([^|]*\|)([^|]*)\|[[:space:]]*$/\1\2|\2/' infile
和awk:
awk 'BEGIN{ FS=OFS="|" } $3 ~/^[[:space:]]*$/ { $3=$2 }1' infile
FS 是F产量S分离器,OFS 是氧输出F产量S分离器;然后我们检查第三列是否为空/制表符/空格,然后更新其内容与第二列相同。然后打印1
。
答案2
使用awk
, 并将第三个字段替换为第二个字段,除非第三个字段包含非空白字符:
$ awk -F '|' 'BEGIN { OFS = FS } $3 !~ /[^[:blank:]]/ { $3 = $2 }; 1' file
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
sed
如果第三个字段为空或仅包含空格,则用于插入第二个字段的编号:
$ sed 's/\([[:digit:]]\{1,\}\)|[[:blank:]]*$/\1|\1/' file
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
注意到第三个字段似乎总是与第二个字段相同,我们也可以选择忽略测试第三个字段的任何内容,而只是将其强制为第二个字段的值。
首先,与awk
:
$ awk -F '|' 'BEGIN { OFS = FS } { $3 = $2 }; 1' file
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
甚至
$ awk -F '|' 'BEGIN { OFS = FS } { print NR, $2, $2 }' file
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
这也重新创建了第一列。
然后用sed
:
$ sed 's/|[^|]*$//; s/[[:digit:]]\{1,\}$/&|&/' file
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
此sed
命令首先删除最后一个字段,然后从被删除字段之前的字段重新创建它。
或者,类似的东西
$ cut -d '|' -f 2 file | sed '=; s/.*/&|&/' | sed 'N; y/\n/|/'
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
它使用 提取数据的第二个字段cut
,然后使用=
insed
枚举行并创建字段的重复项,最后使用正确的分隔符将行号附加到数据。
您还可以在 shell 中结合使用cut
和来执行此操作(使用进程替换):paste
bash
$ paste -d '|' <( cut -d '|' -f 1,2 file ) <( cut -d '|' -f 2 file )
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
答案3
和awk
:
awk -F'|' -v OFS='|' '{if($3=="")$3=$2}1'
一个简单的sed
,如果第二列始终是数字:
sed -E "s/([0-9]*)\|$/\1|\1/"
答案4
使用awk
, 来查看是否$3
为空(或不为空):
awk -F'|' -v OFS='|' '$3 == "" {$3=$2} 1' file
1|100437251|100437251
2|51414204|51414204
3|111651604|111651604
4|8321737|8321737
5|27263401|27263401
- 或者
awk -F'|' -v OFS='|' 'length($3) == 0 {$3=$2} 1' file