替换每行中出现的前 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
我有一个以制表符分隔的遗传变异文件,其中 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