用另一个文件中的数据替换范围

时间:2016-09-09 13:38:30

标签: bash awk sed

我有一个包含以下文本(file1)的文件 -

1SER     CA    1   1.401   0.040   0.887
2GLN     CA    2   1.708  -0.155   1.002
3ALA     CA    3   1.870  -0.103   0.662
4GLU     CA    4   1.829   0.274   0.695

我有一个单独的文件(文件2) -

1MET     CA    1  17.704  15.987  17.370
2ARG     CA    2  17.811  16.145  17.712
3ARG     CA    3  17.634  16.267  18.034
4TYR     CA    4  17.465  16.615  18.002

我的目标是用file2的2-4中的数据替换file1中2-4范围内的字符。

期望的输出 -

1MET     CA    1   1.401   0.040   0.887
2ARG     CA    2   1.708  -0.155   1.002
3ARG     CA    3   1.870  -0.103   0.662
4TYR     CA    4   1.829   0.274   0.695

即。 file2的2-4中的字符放在file1的字节2-4中。

我知道我可以使用cut -c 2-4 | sed ...缩小所需区域的范围 但是我无法从单独的文件中“读取”数据并进行替换。

我觉得awk可能更容易,但请不要基于列的答案。它需要是一个基于文件中字符范围的解决方案(在本例中为2-4)。

添加示例

解决方案应该能够做到这一点 - 文件1 -

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

文件2 -

BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

输出 -

ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

5 个答案:

答案 0 :(得分:3)

如果要替换列,只需存储file1中的数据并将其替换为file2:

   // These rules give anyone, even people who are not users of your app,
    // read and write access to your database

    {
      "rules": {
        ".read": true,
        ".write": true
      }
    }

您还可以存储两个第一列的值,然后将其替换为#34;手动"如delete a column with awk or sed中所示:

$ awk 'FNR==NR {col1[FNR]=$1; col2[FNR]=$2; next} {$1=col1[FNR]; $2=col2[FNR]}1' f1 f2
1SER CA 1 17.704 15.987 17.370
2GLN CA 2 17.811 16.145 17.712
3ALA CA 3 17.634 16.267 18.034
4GLU CA 4 17.465 16.615 18.002

如果您只想替换某些字符,请使用substr()提取这些字符:

$ awk 'FNR==NR {data[FNR]=$1 OFS $2; next} {$0=gensub(/(\s*\S+){2}/,data[FNR],1)}1' f1 f2
1SER CA    1  17.704  15.987  17.370
2GLN CA    2  17.811  16.145  17.712
3ALA CA    3  17.634  16.267  18.034
4GLU CA    4  17.465  16.615  18.002

那是:

$ awk -v start=2 -v len=3 'FNR==NR{data[FNR]=substr($0, start, len); next} {$0=substr($0, 1, 2) data[FNR] substr($0, start+len+1)}1' f2 f1
AABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

答案 1 :(得分:2)

如果按“列”和“字节”表示你实际上是指“字符”,那么:

$ cat tst.awk
BEGIN {
    split(range,r,/-/)
    repS = r[1]
    repL = r[2] - r[1] + 1
    befL = repS - 1
    aftS = repS + repL
}
NR==FNR { rep[NR] = substr($0,repS,repL); next }
{ print substr($0,1,befL) rep[FNR] substr($0,aftS) }

$ awk -v range='2-4' -f tst.awk file2 file1
1MET     CA    1   1.401   0.040   0.887
2ARG     CA    2   1.708  -0.155   1.002
3ARG     CA    3   1.870  -0.103   0.662
4TYR     CA    4   1.829   0.274   0.695
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

$ awk -v range='10-25' -f tst.awk file2 file1
1SER     CA    1  17.704   0.040   0.887
2GLN     CA    2  17.811  -0.155   1.002
3ALA     CA    3  17.634  -0.103   0.662
4GLU     CA    4  17.465   0.274   0.695
AAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAA
AAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAA
AAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAA

以上使用了您的示例的连接作为输入文件。

答案 2 :(得分:2)

pastecut

的解决方案
$ paste -d '' <(cut -c1 file1) <(cut -c2-4 file2) <(cut -c5- file1)
1MET     CA    1   1.401   0.040   0.887
2ARG     CA    2   1.708  -0.155   1.002
3ARG     CA    3   1.870  -0.103   0.662
4TYR     CA    4   1.829   0.274   0.695
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

使用变量:

$ s=10
$ e=25
$ paste -d '' <(cut -c1-$((s-1)) file1) <(cut -c"$s"-"$e" file2) <(cut -c$((e+1))- file1)
1SER     CA    1  17.704   0.040   0.887
2GLN     CA    2  17.811  -0.155   1.002
3ALA     CA    3  17.634  -0.103   0.662
4GLU     CA    4  17.465   0.274   0.695
AAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAA
AAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAA
AAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAA

答案 3 :(得分:0)

TXR Lisp中这个过长的单行代码从两个文件中获取两个懒惰的字符串列表,然后以所需的方式将它们合并为一个。 -t选项将结果值(字符串列表)打印为行。

$ txr -t '[apply mapcar* (ret `@{1 [0]}@{2 [1..4]}@{1 [4..:]}`)
                 (mapcar [chain open-file get-lines] *args*)]' file1 file2
1MET     CA    1   1.401   0.040   0.887
2ARG     CA    2   1.708  -0.155   1.002
3ARG     CA    3   1.870  -0.103   0.662
4TYR     CA    4   1.829   0.274   0.695

原则上,它是一种非常类似的解决方案,使用paste将多个cut流组合在一起(感谢Bash命令替换),除了所有流都在一个过程,使用数据结构,并且file1不会被扫描两次。

变量*args*引用剩余的命令行参数,作为字符串列表。内部mapcar通过open-fileget-lines的链接将这些字符串映射为文件,并从每个字符串中获取字符串的惰性列表。然后将这两个惰性行列表应用于mapcar*函数,以通过(ret ...)生成的匿名函数并行映射其元素。 ret运算符构造一个匿名函数,其参数隐式派生自正文中出现的@1@2编号参数。内插准字符串用于执行从左和右字符串中选择字符的操作。 @1会选择整个左侧字符串。支撑语法@{1 [0]}表示其第一个字符,@{2 [1..4]}表示切片提取。

mapcar*函数是mapcar的惰性版本,这很重要:这可以防止我们在打印之前在内存中构造整个输出。代码对延迟列表的输入进行操作,因此当映射遍历这些列表时进行输入,并且通过-t选项消耗输出列表来驱动行进。也就是说,表达式立即mapcar*中返回一个惰性列表,然后打印出该列表(感谢-t选项),强制执行该操作延迟列表驱动产生它的映射,这会驱动延迟输入列表的消耗,从而驱动源文件的读取。

我们可以看到ret表达式的扩展是什么样的:

$ txr -p '(macroexpand (quote (ret `@{1 [0]}@{2 [1..4]}@{1 [4..:]}`)))'
(lambda (#:arg-01-0003
         #:arg-02-0004 . #:rest-0002)
  [identity (progn #:rest-0002
              `@{#:arg-01-0003 [0]}@{#:arg-02-0004 [1..4]}@{#:arg-01-0003 [4..:]}`)])

ret检查了内容,钻进插值的准字符串以找出&#34;元数&#34;参数。它注意到最高的一个是@2,因此生成一个双参数函数(带尾随参数的rest参数)。参数生成为临时符号,这些符号替换所有出现的编号变量。

我们可以证明mapcar*是懒惰的,比如将它乘以两个增加整数的无限列表,然后只从结果中取出前十个方格:

$ txr -p '(take 10 [mapcar* * (range 0) (range 0)])'
(0 1 4 9 16 25 36 49 64 81)

答案 4 :(得分:-1)

你可以给出

{{1}}

命令尝试:将行连接在一起然后切掉有缺陷的列。

join需要对文件进行排序。