【问题标题】:Find a line with a single word and merge it with the next line找到一个单词的一行并将其与下一行合并
【发布时间】:2021-03-29 12:19:04
【问题描述】:

我有一个无法解决的 grep 问题。

我有什么。 名字和姓氏的列表,例如:

John Doe
Alice Smith
Bob Smith

我的问题。 有时,名字和姓氏是脱节的,例如:

Alice
Smith
Bob Doolittle
Mark
Von Doe //sometimes, there are more than one word on the next line

我想要实现的目标。 将“孤儿”名称与下一行连接起来。

Alice Smith
Bod Doolittle
Mark Von Doe

我已经尝试过的

grep -ozP "^\w+\n\w.+" file | tr '\n' ' '

所以,在这里我让 grep 找到只有一个单词的行并将其与下一行连接起来,即使下一行有多个单词。

它可以正常工作,但前提是孤立词位于文件的开头。 如果它出现在第一行下方,grep 不会发现它。所以一个快速而肮脏的解决方案,我会循环遍历文件并在每次通过后删除一行对我来说不起作用。

【问题讨论】:

  • 那些只使用 1 个名字的人呢? kalzumeus.com/2010/06/17/…
  • 在许多情况下确实需要考虑这一点,但在这里这是不可能的。列表中的姓名来自国家登记处,您不能没有至少一个名字和一个姓氏。

标签: shell awk sed grep tr


【解决方案1】:

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

sed -E 'N;s/^(\S+)\n/\1 /;P;D' file

追加下一行。

如果模式空间中的第一行仅包含一个单词,则将以下换行符替换为空格。

打印/删除第一行并重复。

【讨论】:

    【解决方案2】:

    使用

    awk '
        {f=$2 ? 1 : 0}
        v==1{v=0; print; next} 
        f==0{v=1; printf "%s ", $1; next}
        1
    ' file
    

    输出

    Alice Smith
    Bob Doolittle
    Mark Von Doe
    

    【讨论】:

      【解决方案3】:

      使用这个 Perl 单行代码:

      perl -lane 'BEGIN { $is_first_name = 1; } if ( @F == 1 && $is_first_name ) { @prev = @F; $is_first_name = 0; } else { print join " ", @prev, @F; $is_first_name = 1; @prev = (); }' in_file
      

      Perl 单行代码使用这些命令行标志:
      -e:告诉 Perl 查找内联代码,而不是在文件中。
      -n:循环输入一行一次,默认将其分配给$_
      -l:在执行内联代码之前剥离输入行分隔符(默认为 *NIX 上的"\n"),并在打印时附加它。-a : 在空格或-F 选项中指定的正则表达式上将$_ 拆分为数组@F

      【讨论】:

        【解决方案4】:

        如果awk 可以接受:

        awk '
        NF==1 {printf "%s ",$1; getline; print; next}
        1' names.dat
        

        地点:

        • NF==1 - 如果当前记录中只有一个名称/字段...
        • printf / getline / print / next - 打印字段 #1,读取下一行并打印,然后跳到下一行
        • 1 - 按原样打印所有其他行

        作为单行:

        awk 'NF==1{printf "%s ",$1;getline;print;next}1' names.dat
        

        这会生成:

        Alice Smith
        Bob Doolittle
        Mark Von Doe //sometimes, there are more than one word on the next line
        

        【讨论】:

          【解决方案5】:

          您可以像这样使用 GNU sed

          sed -E -i '/^[^[:space:]]+$/{N;s/\n/ /}' file
          

          the sed demo:

          s='Alice
          Smith
          Bob Doolittle
          Mark
          Von Doe //sometimes, there are more than one word on the next line'
          sed -E '/^[^[:space:]]+$/{N;s/\n/ /}' <<< "$s"
          

          输出:

          Alice Smith
          Bob Doolittle
          Mark Von Doe //sometimes, there are more than one word on the next line
          

          详情:

          • /^[^[:space:]]+$/ 找到没有空格的行
          • {N;s/\n/ /} - 读入下一行,并将带有新行的换行符附加到当前模式空间,然后s/\n/ / 用空格替换此换行符。

          【讨论】:

          • 即使我会使用 awk 解决方案,我也要感谢您的帮助:您的单行代码帮助我解决了我使用 sed 的另一个用例。
          猜你喜欢
          • 2023-03-27
          • 2014-11-05
          • 2020-05-26
          • 2017-07-11
          • 1970-01-01
          • 1970-01-01
          • 2023-01-26
          • 1970-01-01
          • 2015-06-14
          相关资源
          最近更新 更多