当读取一个非常大的以制表符分隔的文件时,该文件看起来像:
. . . A . . . . 3:.:.:20
. . . B . . . . 4:.:30
. . . C . . . . 5:.:.:40:.
. . . D . . . . .:.:.:.
. . . A . . . . 7:.:.:21
. . . B . . . . .:.:.:.
. . . D . . . . .:.:.:.
. . . C . . . . .:.:.
我想把第9列保留为。:.:.:。对于所有其他列4的值,我有一个单独的sed替换查询。所需的输出如下所示:
. . . A . . . . 1:.:.:80
. . . B . . . . 1:.:80
. . . C . . . . 1:.:.:80:.
. . . D . . . . .:.:.:.
. . . A . . . . 1:.:.:80
. . . B . . . . 1:.:.:80
. . . D . . . . .:.:.:.
. . . C . . . . 1:.:80
我现在的伪代码是这样的:
if [column 4 = D]
then
# either replace or just keep original entry
sed '/some replacement query/g' {file}
else
sed '/some other replacement query/g' {file}
fi
我在想,如果我可以在文件中一行一行地读取,也许我可以采用这种方法?但是我又不断地写更新新文件。
我不太确定如何在shell环境中有效地做到这一点。由于最终目标是创建一个大的结束文件,所以我当前的路由创建了两个不同的不正确的版本。也许有一种方法可以在单个sed查询中完成我想要的操作?但那超出了我的专业范围。
编辑:sed替换查询当前查看第9列,并确定字段分隔符的数量(":")并将其替换为相应的值,即3:.:.:20
变为1:.:.:80
,4:.:30
变为1:.:80
,5:.:.:40:.
变为1:.:.:80:.
。字段分隔符之间的值可以是数字0-99或";价值。但是,如果列4 = N,列9必须变成(或者在本例中保持不变).:.:.:.
我当前的替换查询将.:.:.:.
变成1:.:.:80
,这是我不想要的。
sed 's/t(.|[0-9]):(.|([0-9]+)):(.|([0-9]+)):(.|([0-9]+))$/t1:.:.:80/g;
s/t(.|[0-9]):([0-9],[0-9]):(.|([0-9]+)):(.|([0-9]+)):(.|([0-9]+))$/t1:.:.:80:./g;
s/t(.|[0-9]):(.|([0-9]+)):(.|([0-9]+))$/t1:.:80/g'
awk
通常在面向领域的东西上更好,但它可以完成sed
:除非列4是D
提取列9,编辑和附加作为第10列,最后删除第9列。
编辑更新为使用:
作为子字段分隔符(是/
)。
EDIT2:澄清t
不是POSIX或macos。
sed -E -e '
/^([^t]*t){3}Dt/b # special case: dont edit if D in column 4
h # copy line to hold space
s/^([^t]*t){8}([^t]*).*/2/ # set pattern space = column 9
s/^[^:]+/1/ # set first subfield = 1
/([0-9]+){2}|[^:]+$/ s//80/ # set 2nd digit group or last subfield = 80
x # exchange pattern and hold spaces
G # append n+hold space to pattern space
s/n/t/ # replace n with field separator
s/[^t]*t//9 # delete (old) column 9
' -- file
额外的评论:
- 选项卡是字段分隔符;如果你的
sed
不理解t
(GNUsed
可以,但在macos上可能不行)-或者使脚本POSIXly正确-将t
替换为文字制表符 s//80/
中的空正则表达式重新应用上次使用的正则表达式(在/…/
中)- 使用
-E
选项之前扩展正则表达式
使用您展示的示例,请尝试以下awk
程序。
awk '$4!="D"{sub(/^[0-9]+|^./,"1",$9);sub(/[0-9]+$|.$/,"80",$9)} 1' Input_file
或如果您在Input_file中有TAB分隔值,则将BEGIN
节添加到上述程序中,如下所示:
awk '
BEGIN{FS=OFS="t"}
$4!="D"{
sub(/^[0-9]+|^./,"1",$9)
sub(/[0-9]+$|.$/,"80",$9)
}
1
' Input_file
我不确定你是否真的需要对这些映射进行硬编码,但你的问题中没有其他解释,所以,假设你这样做了,那么这将在每个Unix机器的任何shell中使用任何awk:
$ cat tst.awk
BEGIN {
FS=OFS="t"
str2str[".:.:."] = "1:.:80"
str2str[".:.:.:."] = "1:.:.:80"
str2str[".:.:.:.:."] = "1:.:.:80:."
for (str in str2str) {
re = str
gsub(/./,"(.|[0-9]+)",re)
re2str["^("re")$"] = str2str[str]
}
}
$4 != "D" {
for (re in re2str) {
if ($9 ~ re) {
$9 = re2str[re]
}
}
}
{ print }
$ awk -f tst.awk file
. . . A . . . . 1:.:.:80
. . . B . . . . 1:.:80
. . . C . . . . 1:.:.:80:.
. . . D . . . . .:.:.:.
. . . A . . . . 1:.:.:80
. . . B . . . . 1:.:.:80
. . . D . . . . .:.:.:.
. . . C . . . . 1:.:80
这是使用awk
的一个可能的解决方案。
cat file | awk '{if ($4 != "D") { sub($9, "Replaced the ninth field" ); print } else { print }}''
如果第4个字段($4)不是D
,将用字符串替换第9个字段($9
)。
你也可以用一个正则表达式替换$9
,如果你知道最后一个字段的形状足够好,像/d/./d$
之类的。不确定是否也需要反向引用替换
但这可能是个开始。
cat out.tsv | awk '{if ($4 != "D") { sub($9, "Replaced the ninth field" ); print } else { print }}'
. . . A . . . . Replaced the ninth field
. . . B . . . . Replaced the ninth field
. . . C . . . . Replaced the ninth field
. . . D . . . . ./././.
. . . A . . . . Replaced the ninth field
. . . B . . . . Replaced the ninth field
. . . D . . . . ./././.
. . . C . . . . Replaced the ninth field