我有一个这样的文本文件:
test.list
##a
##b
##C
#CHROM 0_62000_1 0_62000_5 0_62070_19 0_62000
我有OLD_SM.list
0_62000_1
0_62000
0_62070_19
和NEW_SM.list
APPLE
BANANA
KIWI
我想用NEW_SM.list.替换test.list中与OLD_SM.list匹配的单词
我更喜欢sed命令,所以我尝试了这样的方法,但不起作用。
paste OLD_SM.list NEW_SM.list | while read OLD_SM NEW_SM; do sed -i "/^#CHROM/s/[[:space:]]${OLD_SM}$/t${NEW_SM}/g" test.list; done
结果我想要
##a
##b
##C
#CHROM APPLE 0_62000_5 KIWI BANANA
使用GNU sed,您可以将单词的开头和结尾与<
>
相匹配。您可以首先根据输入生成一个sed脚本,然后将其传递给sed。输入中必须没有特殊字符。
script=$(
paste OLD_SM.list NEW_SM.list |
sed 's/(.*)t(.*)/s~\<1\>~2~g/'
)
sed -i "/^#CHROM/{ $script }" file.
s/[[:space:]]${OLD_SM}$
-$
匹配行的末尾,所以它永远不会工作。您可以执行s/(^|[[:space:]])$OLD_SM([[:space:]]|$)/1$NEW_SM2/
-匹配行或空格的开头,然后匹配单词,然后匹配空格或行的结尾,然后替换backreference。研究主题:正则表达式和sed中的backreferences。
您可以使用以下paste + awk
解决方案:
awk -v OFS='t' 'NR == FNR { map[$1]=$2; next} $1 == "#CHROM" {for (i=2; i<=NF; ++i) $i in map && $i=map[$i]} 1' <(paste OLD_SM.list NEW_SM.list) test.list
##a
##b
##C
#CHROM APPLE 0_62000_5 KIWI BANANA
扩展形式:
awk -v OFS='t' '
NR == FNR {
map[$1] = $2
next
}
$1 == "#CHROM" {
for (i=2; i<=NF; ++i)
$i in map && $i = map[$i]
}
1' <(paste OLD_SM.list NEW_SM.list) test.list
略有不同:将sed程序构建为bash数组:
sed_opts=()
while read -r old <&3; read -r new <&4; do
sed_opts+=( -e "s/\<$old\>/$new/g" )
done 3< OLD_SM.list 4< NEW_SM.list
sed "${sed_opts[@]}" test.list