shell 使用sed在一组行上添加行

snz8szmq  于 2023-02-05  发布在  Shell
关注(0)|答案(4)|浏览(145)
    • 在下面编辑**

我是bash脚本的新手,很抱歉,如果这个问题已经在其他地方得到了回答,在我做的任何搜索中都找不到它。
例如,我使用sed-i在参数上方添加一行。

for EFP in *.inp; do
    sed -i "/^O */i FRAGNAME=H2ODFT" $EFP
done

它按预期工作。但我希望它只在参数跨多行为真时添加该行,如下所示:

O
C
O
C
FRAGNAME=H2ODFT
O
H
H
FRAGNAME=H2ODFT
O
H
H

请注意,后面跟着C的两个O上面没有添加行。
我尝试了以下方法:

for FILE in *.inp; do
    sed -i "/^O*\nH*\nH */i FRAGNAME=H2ODFT" $EFP
done

我希望它会出现在O-H-H的三行之上,但是什么也没发生,它穿过文件时认为那个参数在文档中找不到。
我曾在其他地方寻找并考虑使用awk,但我无法将其纳入我的头脑。
任何帮助将不胜感激!

编辑

谢谢你的帮助。很抱歉我说的有点不清楚。我已经尝试了很多方法,太多了,不能写在这篇文章里了。我试过awk,perl和sed的解决方案,但是它们都不起作用。
我的输入有一系列的O、C和H,它们被指定了笛卡尔坐标:

C           36.116           34.950           34.657
     C           35.638           34.681           35.883
     C           36.134           33.569           36.703
     C           34.379           34.567           37.522
     N           34.579           35.375           36.476
     N           35.234           33.518           37.706
     O           37.045           32.745           36.559
     H           36.892           34.226           34.415
     O           35.234           38.803           30.513
     H           34.303           39.079           30.567
     C           33.490           35.015           38.608
     H           34.002           35.390           39.503
     H           32.894           34.170           38.974
     H           32.832           35.813           38.245
     C           35.342           32.708           38.920
     H           35.920           33.237           39.688
     H           35.942           31.802           38.772
     H           34.356           32.475           39.340
     O           30.226           35.908           36.744
     H           30.557           36.408           37.490
     H           30.642           36.311           35.982
     O           37.356           40.420           29.232
     H           36.473           40.786           29.286
     H           37.220           39.474           29.189
     O           40.889           37.054           35.401
     H           40.304           36.361           35.706
     H           41.620           36.587           34.995

我试图在一组特定的三行(OHH行)之上输入一个新行。
发布的awk解决方案不起作用,因为它会添加额外的行,而当stage被重置时,不应该有这些行。

C           36.116           34.950           34.657
 C           35.638           34.681           35.883
 C           36.134           33.569           36.703
 C           34.379           34.567           37.522
 N           34.579           35.375           36.476
 N           35.234           33.518           37.706
 O           37.045           32.745           36.559
 H           36.892           34.226           34.415
 O           35.234           38.803           30.513
 H           34.303           39.079           30.567
 C           33.490           35.015           38.608
 H           34.002           35.390           39.503
 H           32.894           34.170           38.974
 H           32.832           35.813           38.245
 C           35.342           32.708           38.920
 H           35.920           33.237           39.688
 H           35.942           31.802           38.772
 H           34.356           32.475           39.340
 FRAGNAME=H2ODFT
 O           30.226           35.908           36.744
 H           30.557           36.408           37.490
 H           30.642           36.311           35.982
 FRAGNAME=H2ODFT
 O           37.356           40.420           29.232
 H           36.473           40.786           29.286
 H           37.220           39.474           29.189
 FRAGNAME=H2ODFT
 O           40.889           37.054           35.401
 H           40.304           36.361           35.706
 H           41.620           36.587           34.995

^tsed是一个打字错误,应该是缩进而不是^t

mspsb9vt

mspsb9vt1#

这里有一个Ruby做到这一点:

ruby -e 'lines=$<.read.split(/\R/)
lines.each_with_index{|line,i| 
    three_line_tag=lines[i..i+2].map{|sl| sl.split[0] }.join
    puts "FRAGNAME=H2ODFT" if three_line_tag == "OHH"
    puts line
}
' file

或者任何awk,同样的方法:

awk '{lines[NR]=$0}
END{
    for(i=1;i<=NR;i++) {
        tag=""
        for(j=0;j<=2;j++) {
            split(lines[i+j],arr)
            tag=tag arr[1]
        }
        if (tag=="OHH")
                print "FRAGNAME=H2ODFT"
        print lines[i]
    }
}
' file

或者Perl:

perl -0777 -pe 's/(^\h*O\h.*\R^\h*H\h.*\R^\h*H\h.*\R?)/FRAGNAME=H2ODFT\n\1/gm' file

任何印刷体:

C           36.116           34.950           34.657
    C           35.638           34.681           35.883
    C           36.134           33.569           36.703
    C           34.379           34.567           37.522
    N           34.579           35.375           36.476
    N           35.234           33.518           37.706
    O           37.045           32.745           36.559
    H           36.892           34.226           34.415
    O           35.234           38.803           30.513
    H           34.303           39.079           30.567
    C           33.490           35.015           38.608
    H           34.002           35.390           39.503
    H           32.894           34.170           38.974
    H           32.832           35.813           38.245
    C           35.342           32.708           38.920
    H           35.920           33.237           39.688
    H           35.942           31.802           38.772
    H           34.356           32.475           39.340
FRAGNAME=H2ODFT
    O           30.226           35.908           36.744
    H           30.557           36.408           37.490
    H           30.642           36.311           35.982
FRAGNAME=H2ODFT
    O           37.356           40.420           29.232
    H           36.473           40.786           29.286
    H           37.220           39.474           29.189
FRAGNAME=H2ODFT
    O           40.889           37.054           35.401
    H           40.304           36.361           35.706
    H           41.620           36.587           34.995

===

    • 就地编辑:**

阅读关于awk的THIS,这是普遍适用的。
这些脚本中的任何一个都写入stdout
您可以将输出重定向到新文件:

someutility input_file >new_file

或者一些(像perl,ruby,GNU awk,GNU sed)有能力进行文件替换。如果你没有这个选项,你不能做:

someutil 'prints to STDOUT' file >file

因为file将在完全读取之前被破坏。
相反,您可以:

someutil 'prints to STDOUT' file > tmp && mv tmp file
wh6knrhe

wh6knrhe2#

这可能对您有用(GNU sed):

sed -Ei -e ':a;N;s/\n/&/2;Ta;/^O(\n.)\1$/i FRAGNAME=H2ODFT' -e 'P;D' file1 file2

在整个文件中打开一个3行窗口,如果所需模式匹配,则插入所需文本行。
注意:\1反向引用与前一行匹配。另外,由于i命令要求以-e选项提供的换行符结尾,因此脚本分为两个单独的部分。
同一解决方案的替代版本:

cat <<\! | sed -Ef - -i file{1..100}
:a
N
s/\n/&/2
Ta
/^O(\n.)\1$/i FRAGNAME=H2ODFT
P 
D
!
mf98qq94

mf98qq943#

如果输入文件不太大,不会导致内存问题,您可以slurp整个文件,然后执行替换。

perl -0777 -pe 's/^O\nH\nH\n/FRAGNAME=H2ODFT\n$&/gm' ip.txt

如果这对你有用,那么你可以添加-i选项来进行就地编辑。问题中显示的正则表达式^O*\nH*\nH *并不清楚。^O\nH\nH\n将精确地匹配具有OHH的三行。根据需要进行调整。

uajslkp6

uajslkp64#

我知道您要求的是sed解决方案,但我有一个基于awk的解决方案。

  • 我们使用stage初始化awk程序,该程序将跟踪“OHH”的进度
  • 如果我们收到另一封信,我们增大stage直到得到OHH,然后,我们打印所需的字符串并重置stage
  • 如果遇到损坏,我们将打印出stage中累积的内容并重置stage
awk '
BEGIN { stage="" }
/^O$/ { if (stage=="") { stage="O\n"; next } }
/^H$/ { if (stage=="O\n") { stage="O\nH\n"; next } }
/^H$/ { if (stage=="O\nH\n") { print "FRAGNAME=H20DFT\nO\nH\nH"; stage=""; next } }
{ print stage $1; stage="" }
' < sample.txt

其中sample.txt包含:

O
C
O
C
O
H
H
O
H
H

相关问题