替换每行中出现的前 3 个字符

Replace first 3 occurrences of a character in each line

我有一个以制表符分隔的遗传变异文件,其中 INFO 列包含许多以分号分隔的标签:

Chr Start   End Ref Alt ExAC_ALL    ExAC_AFR    ExAC_AMR    ExAC_EAS    ExAC_FIN    ExAC_NFE    ExAC_OTH    ExAC_SAS    Otherinfo   QUAL    DP  Chr Start       Ref Alt QUAL    FILTER  INFO
1   15847952    15847952    G   C   .   .   .   .   .   .   .   .   .   241.9   76196   1   15847952    .   G   C   241.9   PASS    AC=2;AF=0;AN=18332;BaseQRankSum=0.731;ClippingRankSum=-0.731;DP=76196;ExcessHet=3.1;FS=0;InbreedingCoeff=-0.0456;MLEAC=2;MLEAF=0;MQ=38.93;MQRankSum=0.515;NEGATIVE_TRAIN_SITE;QD=10.52;ReadPosRankSum=0.89;SOR=0.481;VQSLOD=-1.406        culprit=MQ
1   15847963    15847963    A   C   .   .   .   .   .   .   .   .   .   1607.1  126156  1   15847963    .   A   C   1607.1  PASS    AC=2;AF=0;AN=22004;BaseQRankSum=0.851;ClippingRankSum=-0.419;DP=126156;ExcessHet=3.4904;FS=0;InbreedingCoeff=0.0299;MLEAC=2;MLEAF=0;MQ=59.29;MQRankSum=0.18;QD=1.55;ReadPosRankSum=0.067;SOR=0.651;VQSLOD=0.995        culprit=QD
1   15847964    15847966    GCC -   .   .   .   .   .   .   .   .   .   1607.1  126156  1   15847963    .   AGCC    A   1607.1  PASS    AC=63;AF=0.003;AN=22004;BaseQRankSum=0.851;ClippingRankSum=-0.419;DP=126156;ExcessHet=3.4904;FS=0;InbreedingCoeff=0.0299;MLEAC=55;MLEAF=0.002;MQ=59.29;MQRankSum=0.18;QD=1.55;ReadPosRankSum=0.067;SOR=0.651;VQSLOD=0.995        culprit=QD
1   15847978    15847978    C   T   .   .   .   .   .   .   .   .   .   648.41  234344  1   15847978    .   C   T   648.41  PASS    AC=9;AF=0;AN=25894;BaseQRankSum=-0.572;ClippingRankSum=-0.404;DP=234344;ExcessHet=3.348;FS=2.639;InbreedingCoeff=-0.0098;MLEAC=6;MLEAF=0;MQ=58.71;MQRankSum=-0.456;NEGATIVE_TRAIN_SITE;QD=4.13;ReadPosRankSum=-0.456;SOR=0.452;VQSLOD=-1.238        culprit=QD

我想拆分 INFO 列中的前 3 个以分号分隔的术语:

AC=2;AF=0;AN=18332

这样他们就变成了:

AC=2    AF=0    AN=18332    BaseQRankSum=0.731;ClippingRankSum=-0.731;DP=76196;ExcessHet=3.1;FS=0;InbreedingCoeff=-0.0456;MLEAC=2;MLEAF=0;MQ=38.93;MQRankSum=0.515;NEGATIVE_TRAIN_SITE;QD=10.52;ReadPosRankSum=0.89;SOR=0.481;VQSLOD=-1.406        culprit=M

到目前为止,我已经用 sed 尝试了以下表达式:

sed -i .bk 's/\(A.=.*\);/        /g' allChr_ExAC38.hg38_multianno.txt

但这不会产生任何变化。

理想情况下,我正在寻找一种方法来告诉 sed 将分号 ; 的前 3 次出现替换为 tab,但 's/;/ /g3' 不会好像是这个意思。

能否请您尝试关注,如果对您有帮助,请告诉我。

awk '
FNR==1{
  print;
  next}
{
  num=split($(NF-1),array,";");
  for(i=4;i<=num;i++){
    val=val?val ";"array[i]:array[i]};
    $(NF-1)=array[1] OFS array[2] OFS array[3] OFS val;
    val="";
    =
}
1
' OFS="\t"  Input_file

使用 Perl 代替 sed:

perl -i.bk -pe '$c = 0; s/;/\t/ while $c++ < 3' -- file.txt

正则表达式中的 .* 是贪心的,会匹配行中尽可能多的文本,直到最后一个分号之前(但不会超过,因为那样的话整个正则表达式将无法匹配完全没有)。

您不能混合使用 /3/g;后者的意思是,替换每一行中出现的 all 次,因此它与 /3 直接不一致,后者表示一行中最多只替换三次。

不过,

"No changes" 似乎是错误的;如果您的正则表达式完全匹配,则匹配行的最后一个分号将被替换。

一些正则表达式引擎支持非贪婪匹配,但 sed 不是其中之一。只要有一个分隔符可以用来限制贪婪,无论如何使用它都是一个更好的解决方案。在您的情况下,只需将 . 替换为 [^;] 即可表示 "any character except (newline or) semicolon" 而不是 "any character (except newline)."

sed 's/\(A.=[^;]*\);/        /3' allChr_ExAC38.hg38_multianno.txt

(这将打印到标准输出以进行验证;一旦您看到结果正确,请放回 -i .bk。)

根据您的示例数据,或许可以考虑将表达式中剩余的 . 替换为 [A-Z],并将 [^;] 替换为 [^;=] 甚至 [0-9]。正则表达式越具体越好。

你可以试试这个 awk

awk '{for(i=1;i<4;i++)sub(";","\t")}1' infile

这可能适合您 (GNU sed):

sed -i.bak 's/;/\n/3;h;y/;/\t/;G;s/\n.*\n/\t/' file

用换行符替换第三个;,复制该行,用\t替换所有;,追加副本并替换结尾第一行到第二行中间有一个\t.

根据定义,一行由换行符划分,除非由程序员引入,否则行不能包含换行符。

如果出现的次数是合理的,你可以通过管道多次发送 sed 即

sed -E -e 's/[0-9]{4}/****/'| sed -E -e 's/[0-9]{4}/****/'| sed -E -e 's/[0-9]{4}/****/'

将像这样屏蔽前 3 组 4 位信用卡号

Input:
1234 5678 9101 1234  

Output:
**** **** **** 1234