【问题标题】:Replace character string between new lines替换新行之间的字符串
【发布时间】:2019-01-07 14:56:10
【问题描述】:

我正在尝试编辑一个文件,以便删除两侧带有新行的字符串。

我的文件看起来像:

ENSG00000000460_chr1.dat        
varX data data data data data 
varX data data data data data 
varX data data data data data 

ENSG00000005801_chr11.dat        

ENSG00000006007_chr16.dat        

ENSG00000006607_chr2.dat        
varX data data data data data 
varX data data data data data 

ENSG00000010219_chr12.dat        

ENSG00000011052_chr17.dat 

我试图获得的输出将删除两侧有新行的行(并删除新行),导致输出如下所示:

ENSG00000000460_chr1.dat        
varX data data data data data 
varX data data data data data 
varX data data data data data 

ENSG00000006607_chr2.dat        
varX data data data data data 
varX data data data data data 

我在 sed 中尝试过各种想法,但都返回:

sed 's/[\na-zA-Z0-9\n]//g' file.txt | head
_.
. .- . . . . .
. .- . . . . .
-. .- . . . . .
-. .- . . . . .

_.

_.

我也试过了:

sed 's/[\n][a-zA-Z0-9][\n]//g' file.txt | head 

但这会按原样返回文件:

ENSG00000000460_chr1.dat        
varX data data data data data 
varX data data data data data 
varX data data data data data 

ENSG00000005801_chr11.dat        

ENSG00000006007_chr16.dat        

ENSG00000006607_chr2.dat        
varX data data data data data 
varX data data data data data 

ENSG00000010219_chr12.dat        

ENSG00000011052_chr17.dat 

【问题讨论】:

  • 你尝试输出什么。
  • 我从来没有用过sed,但如果它像普通的正则表达式一样工作,那我不明白为什么这么多人不理解[ ]的含义。
  • [\na-zA-Z0-9\n] 表示:1 of (\n 或字母或数字)
  • 请查看[ ] 的含义。因为[^a-zA-Z0-9$] 表示任何 1 不是字母、数字或 $ 的符号
  • 我也尝试使用 [ ] (我现在已将其添加到示例中)。

标签: regex bash sed replace find


【解决方案1】:

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

sed ':a;$!{N;/^$/M!ba};/\n./!d' file

收集模式空间中的行,直到文件结尾或空行。如果集合有两个或更多非空行,请不要删除它们,即打印这些集合。删除由单行后跟空行组成的集合。

【讨论】:

  • 我一直在等待有人提出一个不错的 sed 解决方案,因为我自己想不出任何优雅的解决方案。阅读手册后,我仍然对M 修饰符感到有些困惑。你能详细说明一下吗? :)
  • @Pesa GNU sed 中正则表达式后面的M 标志是多行标志。当模式空间包含通常通过NHG 和替代方式引入的\n 时,它允许使用^$ 来表示一行的开始和结束/转置命令。见here
  • 感谢您的解释。 ++ 很好的解决方案。
【解决方案2】:

可能不是最好的解决方案,但这里有另一个awk 解决方案:

awk '/^$/{c=0;next} ++c==2{print (f++?RS:"") s} {s=$0} c>1' file

【讨论】:

    【解决方案3】:

    一般注意事项:以下解决方案要求您使用 Unix 行尾 (\n) 而不是 Dos 行尾 (\r\n)。通过复制输入,观察到 Dos 行尾。 (dos2unix 可能会派上用场)

    awk 'BEGIN{RS="";FS="\n";ORS="\n\n"}(NF>1)' file  # introduces extra line at end
    awk 'BEGIN{RS="";FS="\n";}(NF>1){print (p?"\n":"")$0;p=1}' file  # no extra line
    

    它是如何工作的:通过将记录分隔符RS 设置为空,所有段落都被视为记录。问题是,他们有多少行。答案是通过计算一条记录中的字段数来给出的,其中每个字段由字段分隔符FS="\n" 分隔。因此,如果每条记录的 NF 字段数大于 1,我们希望打印该记录。

    sed  '/^$/!{H;d};{x;/.\n/!d}' file                # introduces extra line at start
    

    它是如何工作的: SED 有两个记忆。模式空间是您执行所有操作的地方,而保持空间是长期记忆。这个想法是始终有一个空的模式空间,并使用H 将所有文本附加到保留空间。如果读取了新行并且模式空间保持为空。通过与模式空间 (x) 交换来处理保持空间。如果它包含一个字符后跟一个换行符,则打印它,否则删除它。


    awk:

    RS:RS字符串值的第一个字符为输入记录分隔符; 默认情况下。如果RS 包含多个字符,则未指定结果。 如果RS 为空,则记录由 加上一个或多个空行组成的序列分隔,前导或尾随空行不应导致输入的开头或结尾处为空记录,并且无论FS 的值是什么, 都应始终是字段分隔符。

    FS: 输入字段分隔正则表达式;默认为

    ORS:打印语句输出记录分隔符;默认为

    来源:Posix AWK standard

    sed:

    [2addr]d:删除模式空间,开始下一个循环。

    [2addr]H: 附加到保持空间,后跟模式空间的内容。

    [2addr]x:交换模式的内容并保留空格。

    来源:Posix SED standard

    【讨论】:

    • 我认为这在 FS=\n 中的 \n 之前缺少一个引号。我刚刚尝试过,它只是返回文件不变。
    • @LynseyHall 确实如此。固定
    【解决方案4】:

    请您尝试以下操作:

    awk '/_chr/ && flag{flag=val=""} /_chr/{val=$0;next} val && NF && !/_chr/{if(val){print val;val=""};flag=1} flag;END{if(!flag && val && val!~/chr/){print val}}'  Input_file
    

    现在也添加非单线形式的解决方案。

    awk '
    /_chr/ && flag{
      flag=val=""
    }
    /_chr/{
      val=$0
      next
    }
    val && NF && !/_chr/{
      if(val){
        print val
        val=""
      }
      flag=1
    }
    flag
    END{
      if(!flag && val && val!~/chr/){
        print val}
    }'   Input_file
    

    解释:在这里也添加解释。

    awk '
    /_chr/ && flag{                   ##Checking condition if string _chr is present in a line and flag is SET then do following.
      flag=val=""                     ##Nullifying variable flag and val here.
    }
    /_chr/{                           ##Checking if a line has string _chr in it then do following.
      val=$0                          ##Setting variable val value to $0(current line) here.
      next                            ##using next keyword to skip all further statements from here.
    }
    val && NF && !/_chr/{             ##Checking condition if variable val is SET AND line is NOT NULL and line is not having _chr in it then do following.
      if(val){                        ##If variable val value is NOT NULL then do following.
        print val                     ##Printing variable val here.
        val=""                        ##Nullifing val here.
      }
      flag=1                          ##Setting flag variable to SET here.
    }
    flag                              ##Checking condition if value of flag is SET then print current line.
    END{                              ##Starting END block of awk here.
      if(!flag && val && val!~/chr/){ ##Checking condition if flag is NULL and variable val is SET and val value is NOT _chr then print value of val in next line.
        print val}
    }' Input_file                     ##Mentioning Input_file name here.
    

    【讨论】:

    • 这确实有效!我可以要求解释一下它在做什么吗?
    • @LynseyHall,在上面,几分钟后会添加。
    • @LynseyHall,请查看我的解释部分,如有任何疑问,请告诉我。
    猜你喜欢
    • 2017-07-18
    • 2018-01-14
    • 2012-06-21
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 2022-10-24
    • 1970-01-01
    • 2015-04-08
    相关资源
    最近更新 更多