我想使用sed
带有通配符表达式的命令在 TSV 文件中插入字符:
我有一个这样的文件:
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl 3 16887238
我想_All
在第三列中所有文本的末尾添加如下内容:
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl_All 3 16887238
我正在使用这个命令,但它不起作用:
sed -i 's/Total_Soil_\(.*\)/&_\1_All/g' top1.txt
这只是一个示例文件,可以有任何内容来代替S
、Pa
和Cl
。
答案1
我会避免使用表格数据,sed
因为很难正确定位要修改的数据中的确切位置。该sed
实用程序更适合处理文本等非结构化数据。
使用磨坊主(mlr
;专门用于处理结构化数据的工具)将字符串附加到每个 TSV 记录字段_All
中的数据末尾:Trait
$ mlr --tsv put '$Trait .= "_All"' file
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl_All 3 16887238
mlr
与其选项一起使用-I
以就地进行更改。
您是否需要确保仅当字段以 string 开头时才修改该字段Total_Soil
,然后使用
mlr --tsv put '$Trait =~ "^Total_Soil" { $Trait .= "_All" }' file
使用awk
,将字符串附加_All
到每条记录的第三个制表符分隔字段中的数据末尾:
$ awk -F '\t' 'BEGIN { OFS=FS } NR > 1 { $3 = $3 "_All" }; 1' file
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl_All 3 16887238
1
代码末尾的尾随awk
会导致无条件输出修改后的记录。在某种程度上,它是一种速记写作方式{ print }
。请注意,我们明确需要避免修改标头。我们通过NR > 1
仅在测试评估结果为时才测试使用和修改该字段来做到这一点真的(NR
是当前记录的序号)。
将输出重定向到新文件,然后将新文件重命名为原始名称。或者,如果您使用的是 GNU awk
,请-i inplace
按照说明使用在另一个问题+答案中。
同样,如果您需要确保仅修改以 string 开头的第三个字段Total_Soil
,则使用
awk -F '\t' 'BEGIN { OFS=FS } NR > 1 && $3 ~ /^Total_Soil/ { $3 = $3 "_All" }; 1' file
使用 Perl 的方式与以下几乎相同awk
:
$ perl -F'\t' -e 'BEGIN { $" = "\t" } if ($. > 1) { $F[2] .= "_All" } print "@F"' file
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl_All 3 16887238
确保我们只修改Total_Soil
数据:
perl -F'\t' -e 'BEGIN { $" = "\t" } if ($. > 1 && $F[2] =~ /^Total_Soil/) { $F[2] .= "_All" } print "@F"' file
答案2
使用乐(以前称为 Perl_6)
~$ raku -ne 'BEGIN put get; my @a = .split("\t"); @a.[2] = @a.[2] ~ "_All"; put @a.join("\t");' file
Raku 是 Perl 编程语言家族的成员。 Raku 的一个优点是对内置 Unicode 的高级支持,不需要外部库(或特殊标志)。
上面是@Kusalananda 优秀的 Perl(5) 答案的相当直接的翻译。-ne
使用Raku 的非自动打印“逐行”命令行标志。要逐字打印标题行,请使用BEGIN
移相器将其put get
放入(打印使用终止符)第一行get
。
逐行命令的主体工作方式如下:声明一个数组,并将选项卡上的my @a
输入行分配给它[是] 的缩写。$_
.split("\t")
.split("\t")
$_.split("\t")
获取@a.[2]
第三个元素(即列)并用相同的元素覆盖它,@a.[2] ~ "_All"
并用波浪号与尾随_All
字符串连接。
然后取出所有@a
元素,join
在选项卡上重新组合在一起,然后输出put
。
输入示例:
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl 3 16887238
示例输出:
Marker Pvalue Trait Chr Pos
S3_16887238 6.172847e-09 Total_Soil_S_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Pa_All 3 16887238
S3_16887238 6.172847e-09 Total_Soil_Cl_All 3 16887238
答案3
使用 sed 你可以这样做:
sed 's/Total_Soil_[^[:blank:]]*/&_All/' top1.txt
要内联执行此操作,请-i
在 sed 之后添加
编辑: 替换[^ ]
为以[^[:blank:]]
匹配除以下内容之外的所有内容空间和选项卡。