找到包含单个单词的一行并将其与下一行合并

Find a line with a single word and merge it with the next line

我有一个无法解决的 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 不会发现它。所以一个快速而肮脏的解决方案,我会循环遍历文件并在每次通过后删除一行对我来说不起作用。

您可以像这样使用 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:]]+$/ 找到没有白色的行space
  • {N;s/\n/ /} - 读取下一行,并将带有此新行的换行符附加到当前模式 space,然后 s/\n/ / 将此换行符替换为 space.

使用

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

输出

Alice Smith
Bob Doolittle
Mark Von Doe

如果awk可以接受:

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

其中:

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

单线:

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

这会生成:

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

使用这个 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

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

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

追加下一行。

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

Print/delete 第一行并重复。