替换列并保留间距

替换列并保留间距

这是后续unix:用另一个文件中的单个值替换一个文件中的一整列

我试图用另一个文件 (file2) 中的一个特定值替换文件 (file1) 的一列。

file1 的结构如下:

HETATM    8  P   FAD B 600      98.424  46.244  76.016  1.00 18.65
HETATM    9  O1P FAD B 600      98.634  44.801  75.700  1.00 17.69 O  
HETATM   10  O2P FAD B 600      98.010  46.640  77.387  1.00 15.59 O  
HETATM   11 H5B1 FAD B 600      96.970  48.950  72.795  1.00 -1.00 H  

我绝对需要保留这个结构。

file2 的结构如下:

1 27, -81.883, 4.0
5 48, -67.737, 20.0
1 55, -72.923, 4.0
4 27, -62.64, 16.0

我注意到 awk 是“行为不当”并且丢失了我的 pdb 文件的格式,这意味着而不是:

HETATM    1  PA  FAD B 600      95.987  47.188  74.293  1.00 -73.248

我明白了

HETATM 1 PA FAD B 600 95.887 47.194 74.387 1.00 -73.248 

我努力了:

file1="./Min1_1.traj_COP1A_.27.pdb"
file2="./COP1A_report1"
value="$(awk -F, 'NR==1{print $2;exit}' $file2)"
#option 1: replaces the column I want but messes up the format
awk -F ' ' '{$11 = v} 1' v="$value" $file1 >TEST1
#option 2: keeps the format but adds the value at the end only
awk -F ' ', '{$2 = v} 1' v="$value" $file1 >TEST2
awk -F, '{$11 = v} 1' v="$value" $file1 >TEST3

我猜这是因为 pdb 文件的所有列没有相同的分隔符,并且 awk 没有按照我想要的方式处理它。

有什么想法可以“驯服” awk 来解决这个问题或使用其他什么命令吗?

答案1

使用正则表达式([^[:blank:]]即非空白)并替换第一个11匹配项:

awk '{print gensub (/[^[:blank:]]+/, v, 11)}' v="$value" infile

sed与:相同

sed "s/[^[:blank:]]\{1,\}/${value}/11" infile

另一种方式,如果您的文件具有固定长度字段并且您知道每个字段的“位置”(例如,假设示例文件中只有空格,则第 11 个字段占用 4 个字符,每行从第 57 到第 60)

awk '{print substr($0,1,56) v substr($0,61)}' v=$value file

或者

sed -E "s/^(.{56}).{4}(.*)$/\1${value}\2/" infile

答案2

使用 GAWK 4,您可以通过显式分割字符串(或整行)并迭代分割结果(字段和分隔符)以进行输出来保留字段分隔符。

此示例使用FPAT(指定字段结构的正则表达式) andpatsplit()但可以使用FS(指定字段分隔符或包含单个空格来表示 的正则表达式[ \t\n]+) andsplit()代替。

gawk "v=$value" '{n = patsplit($0, arr, FPAT, seps); arr[11] = v; for (i = 0; i <= n; i++) {printf "%s%s", a[i], seps[i]}; print ""}'

请注意,它将a[0]始终为空,seps[0]将包含任何前导分隔符,并且seps[n]将是输入行末尾的任何分隔符字符(空格)。

这是更易读的形式的单行代码:

gawk "v=$value" '
    {
        n = patsplit($0, arr, FPAT, seps); 
        arr[11] = v; 
        for (i = 0; i <= n; i++) {
            printf "%s%s", a[i], seps[i]
        }; 
        print ""
    }'

答案3

我愿意sed为您的任务使用:

file1="./Min1_1.traj_COP1A_.27.pdb"
file2="./COP1A_report1"
IFS=',' read -r a value b <"$file2"
#for second field:
sed "s/.[0-9]\b/$value/" "$file1" > TEST1
#for 11th field:
sed "s/\S.\.[0-9]\{2\}\b/$value/" "$file1" > TEST1

相关内容