【问题标题】:How to replace some particular column values in a file from the columns of other file如何从其他文件的列中替换文件中的某些特定列值
【发布时间】:2020-09-25 15:37:00
【问题描述】:

我知道类似的问题已经在 SO 上回答了很多次。 (一个例子是here: https://stackoverflow.com/questions/7846476/replace-column-in-one-file-with-column-from-another-using-awk

但是,这对我来说是独一无二的,因为我需要处理特定的模式。

我想要更新的文件 1 的标题是

  3    6  0  6.0361821  0.0000000  0.0000000  0.0000000  0.0000000  0.0000000
      0.994429353    0.000000000    0.000000000
      0.000000000    0.994429353    0.000000000
      0.000000000    0.000000000    2.469627493
           1  'A '    63548.626894188397     
           2  'B '    169717.29799472401     
           3  'C '    25598.367262405900     
    1    2      0.7458220147      0.7458220147      1.8031927376   << need to be updated from here
    2    2      0.2486073382      0.2486073382      0.6664347554
    3    1      0.2486073382      0.2486073382      2.2628589536
    4    1      0.7458220147      0.7458220147      0.2067685394
    5    3      0.7458220147      0.7458220147      1.0275486366
    6    3      0.2486073382      0.2486073382      1.4420788564  << upto here
 T
     21.3496599      0.0000000      0.0000000
      0.0000000     21.3496599      0.0000000
      0.0000000      0.0000000     24.1101752
    1
     -7.6119990     -0.0000000      0.0000000
      0.0000000     -7.6119990      0.0000000
      0.0000000      0.0000000     -7.0331945
    2
     -7.6119990      0.0000000      0.0000000
     -0.0000000     -7.6119990      0.0000000
      0.0000000      0.0000000     -7.0331945
    3
      3.4711749      0.0000000      0.0000000

我需要将file1$2$3$4$ESPi"th 行更新为"$ESPf"th$1$2$3(提到@9876543333)以下)。 file1 中的空格在更新时不应更改。这里$ESPi"th"$ESPf"th 分别代表第8 行和第13 行,并且大小写不同。

file2 是

0.750000000 0.750000000 0.730147661   << with these data 
0.250000000 0.250000000 0.269852339
0.250000000 0.250000000 0.916275414
0.750000000 0.750000000 0.083724586
0.750000000 0.750000000 0.416074343
0.250000000 0.250000000 0.583925657  < upto these data

我已经尽力做好我的工作了。

#!/bin/bash
for j in `seq "$ESPi" 1 "$ESPf"`    # ESPi and ESPf are 8 and 13, respectively here and change case by case.
do
ESP1=$(cat file1 | head -n "$j" | tail -n 1 | awk '{print $3}')
ESP2=$(cat file1 | head -n "$j" | tail -n 1 | awk '{print $4}')
ESP3=$(cat file1 | head -n "$j" | tail -n 1 | awk '{print $5}')

for k in `seq 1 1 "$NELEMENTS"`  # $NELEMENTS is six here.
do
qeIN1=$(cat file2 | head -n "$k" | tail -n 1 | awk '{print $1}')
qeIN2=$(cat file2 | head -n "$k" | tail -n 1 | awk '{print $2}')
qeIN3=$(cat file2 | head -n "$k" | tail -n 1 | awk '{print $3}')
sed  's/'$ESP1'/'$qeIN1'/g' file1
sed  's/'$ESP2'/'$qeIN2'/g' file1
sed  's/'$ESP3'/'$qeIN3'/g' file1
done
done

这给了我

3    6  0  6.0361821  0.0000000  0.0000000  0.0000000  0.0000000  0.0000000
      0.994429353    0.000000000    0.000000000
      0.000000000    0.994429353    0.000000000
      0.000000000    0.000000000    2.469627493
           1  'A '    63548.626894188397
           2  'B '    169717.29799472401
           3  'C '    25598.367262405900
    1    2      0.7458220147      0.7458220147      1.8031927376
    2    2      0.750000000      0.750000000      0.6664347554
    3    1      0.750000000      0.750000000      2.2628589536
    4    1      0.7458220147      0.7458220147      0.2067685394
    5    3      0.7458220147      0.7458220147      1.0275486366
    6    3      0.750000000      0.750000000      1.4420788564
 T
     21.3496599      0.0000000      0.0000000
      0.0000000     21.3496599      0.0000000
      0.0000000      0.0000000     24.1101752
    1
     -7.6119990     -0.0000000      0.0000000
      0.0000000     -7.6119990      0.0000000
      0.0000000      0.0000000     -7.0331945
    2
     -7.6119990      0.0000000      0.0000000
     -0.0000000     -7.6119990      0.0000000
      0.0000000      0.0000000     -7.0331945
    3
      3.4711749      0.0000000      0.0000000

预期的输出是

  3    6  0  6.0361821  0.0000000  0.0000000  0.0000000  0.0000000  0.0000000
      0.994429353    0.000000000    0.000000000
      0.000000000    0.994429353    0.000000000
      0.000000000    0.000000000    2.469627493
           1  'A '    63548.626894188397
           2  'B '    169717.29799472401
           3  'C '    25598.367262405900
    1    2      0.750000000      0.750000000      0.730147661
    2    2      0.250000000      0.250000000      0.269852339
    3    1      0.250000000      0.250000000      0.916275414
    4    1      0.750000000      0.750000000      0.083724586
    5    3      0.750000000      0.750000000      0.416074343
    6    3      0.250000000      0.250000000      0.583925657
 T
     21.3496599      0.0000000      0.0000000
      0.0000000     21.3496599      0.0000000
      0.0000000      0.0000000     24.1101752
    1
     -7.6119990     -0.0000000      0.0000000
      0.0000000     -7.6119990      0.0000000
      0.0000000      0.0000000     -7.0331945
    2
     -7.6119990      0.0000000      0.0000000
     -0.0000000     -7.6119990      0.0000000
      0.0000000      0.0000000     -7.0331945
    3
      3.4711749      0.0000000      0.0000000

我正在寻找 shell (bash) 脚本。

【问题讨论】:

  • 将您的脚本复制/粘贴到shellcheck.net,修复它告诉您的问题,如果您仍然需要帮助,请重新发布。

标签: awk sed cut


【解决方案1】:
gawk 'FNR==NR{ a[i++]=$0 }
      FNR!=NR && FNR>=8 && FNR<=13{ split(a[j++],b); $3=b[1]; $4=b[2]; $5=b[3]; }
      FNR!=NR{ print $0 }'  file2 file1

FNR = 当前输入文件中的输入记录编号。 NR = 目前看到的输入记录总数。

第 8 行和第 13 行在此脚本中是固定的,因为没有提供有关如何确定这些值的信息。

编辑:

我忘了保留空白,下一个应该这样做:

gawk 'FNR==NR{ a[i++]=$0 }
      FNR!=NR && FNR>=8 && FNR<=13{ split(a[j++],b); sub($3,b[1]); sub($4,b[2]); sub($5,b[3]); }
      FNR!=NR{ print $0 }'  file2 file1

【讨论】:

  • gawk 似乎没有根据需要保留空格,所以我作弊了......(见其他答案)
  • @alaniwi gawk 如果您编写旨在保留空格的代码,则可以很好地保留空格。这里的问题是脚本,而不是工具。
  • 是的,Luuk 的新脚本运行良好,谢谢。 shell 脚本倾向于调用 awk 和 sed 之类的工具而不是 python 仍然只是一个传统问题。我认为这些天它与操作系统一起安装得相当可靠,但它仍然感觉像是在作弊:-)
  • @alaniwi 这不是传统问题,它只是更简单、更清晰。我发布了一个 awk 答案,以便您了解我的意思。
  • @EdMorton 我可以理解基于启动开销或可移植性的论点,但“更清晰”听起来主要是熟悉度问题。
【解决方案2】:
#!/bin/bash

ESPi=8
ESPf=13

python > file1.new <<EOF
import sys, re
write = sys.stdout.write
espi = $ESPi
espf = $ESPf
repls = {2:0, 3:1, 4:2}
with open("file1") as f1, open("file2") as f2:
    for i in range(espi - 1): write(next(f1))
    for i in range(espf - espi + 1):
        line = next(f1)
        toks = next(f2).split()
        for col, rcol in repls.items():
            pat = "(\s*)((\S+\s+){{{col}}})(\S+)(.*)".format(col=col)
            repl = r"\g<1>\g<2>{val}\g<5>".format(val=toks[rcol])
            line = re.sub(pat, repl, line)
        write(line)
    for line in f1: write(line)
EOF

mv file1.new file1

【讨论】:

    【解决方案3】:

    这将在每个 UNIX 机器上的任何 shell 中使用任何 awk 来工作:

    $ cat tst.awk
    NR==FNR { new[NR]=$0; next }
    (espi <= FNR) && (FNR <= espf) {
        split(new[FNR-espi+1],vals)
        i = 0
        while ( match($0,/[^[:space:]]+/) ) {
            printf "%s%s", substr($0,1,RSTART-1), (++i >= 3 ? vals[i-2] : substr($0,RSTART,RLENGTH))
            $0 = substr($0,RSTART+RLENGTH)
        }
    }
    { print }
    

    .

    $ awk -v espi=8 -v espf=13 -f tst.awk file2 file1
      3    6  0  6.0361821  0.0000000  0.0000000  0.0000000  0.0000000  0.0000000
          0.994429353    0.000000000    0.000000000
          0.000000000    0.994429353    0.000000000
          0.000000000    0.000000000    2.469627493
               1  'A '    63548.626894188397
               2  'B '    169717.29799472401
               3  'C '    25598.367262405900
        1    2      0.750000000      0.750000000      0.730147661
        2    2      0.250000000      0.250000000      0.269852339
        3    1      0.250000000      0.250000000      0.916275414
        4    1      0.750000000      0.750000000      0.083724586
        5    3      0.750000000      0.750000000      0.416074343
        6    3      0.250000000      0.250000000      0.583925657
     T
         21.3496599      0.0000000      0.0000000
          0.0000000     21.3496599      0.0000000
          0.0000000      0.0000000     24.1101752
        1
         -7.6119990     -0.0000000      0.0000000
          0.0000000     -7.6119990      0.0000000
          0.0000000      0.0000000     -7.0331945
        2
         -7.6119990      0.0000000      0.0000000
         -0.0000000     -7.6119990      0.0000000
          0.0000000      0.0000000     -7.0331945
        3
          3.4711749      0.0000000      0.0000000
    

    【讨论】:

    • 您愿意分享一下为什么我的最后一个解决方案不能与“任何 shell 中的任何 awk”一起使用吗?
    • 我没看过。是什么让你认为它不会?
    • @Luuk 现在查看了它,它不会在任何 awk 中工作,因为例如 sub($4,b[2]) 不会用 b[2] 替换 $4,它会替换第一个将$4 描述的正则表达式与b[2] 匹配的字符串,因此如果它匹配$4 描述的正则表达式,它可以替换$3 的部分/全部。例如,如果$312345 并且$42.4 并且b[2]97 那么sub($4,b[2]) 将把$3 变成1975 并留下$4-。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 2020-01-04
    • 2017-04-01
    • 1970-01-01
    • 2023-01-03
    • 1970-01-01
    相关资源
    最近更新 更多