如何从命令行将每两行合并为一行?

Posted

技术标签:

【中文标题】如何从命令行将每两行合并为一行?【英文标题】:How to merge every two lines into one from the command line? 【发布时间】:2012-03-25 04:51:31 【问题描述】:

我有一个格式如下的文本文件。第一行是“KEY”,第二行是“VALUE”。

KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

我需要与键在同一行中的值。所以输出应该是这样的......

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

如果我可以使用诸如$, 之类的分隔符会更好:

KEY 4048:1736 string , 3

如何将两行合二为一?

【问题讨论】:

有很多方法可以做到这一点!我已经完成了little bench with pr, paste, awk, xargs, sed and pure bash! (xargs 比较慢,比bash 慢!) 【参考方案1】:

sed、awk、grep 的替代品:

xargs -n2 -d'\n'

当您想要连接 N 行并且只需要以空格分隔的输出时,这是最好的。

我最初的答案是xargs -n2,它以单词而不是行分隔。 -d(GNU xargs 选项)可用于按任何单数字符分割输入。

【讨论】:

【参考方案2】:

使用 vim 的另一种方法是:

:g/KEY/join

这会将join(到它下面的行)应用于其中包含单词KEY 的所有行。结果:

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

【讨论】:

【参考方案3】:

使用vim的另一种解决方案(仅供参考)。

解决方案 1

在vim中打开文件vim filename,然后执行命令:% normal Jj

这个命令很容易理解:

% : 对于所有行, 正常:执行正常命令 Jj : 执行 Join 命令,然后跳转到下一行

之后,保存文件并使用:wq退出

解决方案 2

在shell中执行命令vim -c ":% normal Jj" filename,然后保存文件并以:wq退出。

【讨论】:

还有norm!normal 更健壮,以防J 被重新映射。 +1 为 vim 解决方案。 @qeatzy 谢谢你教我。很高兴知道。 ^_^【参考方案4】:

在我需要合并两行(以便于处理)但允许数据超过特定的情况下,我发现这很有用

data.txt

string1=x
string2=y
string3
string4
cat data.txt | nawk '$0 ~ /string1=/  printf "%s ", $0; getline; printf "%s\n", $0; getline   print ' > converted_data.txt

输出如下:

converted_data.txt

string1=x string2=y
string3
string4

【讨论】:

【参考方案5】:

使用paste 与glenn jackman's answer 的细微差别:如果-d 分隔符选项的值包含多个字符,paste 将逐个循环字符,并与-s 选项结合使用在处理相同的输入文件时继续这样做。

这意味着我们可以使用任何我们想要的分隔符加上转义序列\n 来一次合并两行。

使用逗号:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string,1
KEY 4192:1349 string,1
KEY 7329:2407 string,2
KEY 0:1774 string,1

和美元符号:

$ paste -s -d '$\n' infile
KEY 4048:1736 string$3
KEY 0:1772 string$1
KEY 4192:1349 string$1
KEY 7329:2407 string$2
KEY 0:1774 string$1

这个不能做的是使用由多个字符组成的分隔符。

作为奖励,如果 paste 符合 POSIX,这不会修改文件中最后一行的换行符,因此对于具有奇数行的输入文件,如

KEY 4048:1736 string
3
KEY 0:1772 string

paste 不会在最后一行添加分隔符:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string

【讨论】:

【参考方案6】:

awk:

awk 'NR%2printf "%s ",$0;next;1' yourFile

注意,输出的末尾有一个空行。

sed:

sed 'N;s/\n/ /' yourFile

【讨论】:

【参考方案7】:

你可以像这样使用xargs

xargs -a file

【讨论】:

% cat > file a b c % xargs -a file a b c % 为我工作 它做了某事,是的,但不是 OP 要求的。具体来说,它连接尽可能多的行。你实际上可以用xargs -n 2 得到你想要的,但这个答案根本没有解释这一点。【参考方案8】:

试试下面这行:

while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file

将分隔符放在中间

"$line1 $line2";

例如如果分隔符是|,那么:

"$line1|$line2";

【讨论】:

这个答案没有添加任何未在Hai Vu's answer 中提供的内容,这些内容是在您之前 4 年发布的。 我部分同意,我尝试添加解释和更通用的它也不会编辑旧文件。感谢您的建议【参考方案9】:

杀死狗的方法比绞死更多。 [1]

awk 'key=$0; getline; print key ", " $0;'

将你喜欢的任何分隔符放在引号内。


参考资料:

    最初是“给猫剥皮的多种方法”,后来恢复为更古老的、可能起源于与宠物无关的表达方式。

【讨论】:

【参考方案10】:

一种更通用的解决方案(允许加入多个后续行)作为 shell 脚本。这在每个之间添加了一条线,因为我需要可见性,但这很容易解决。此示例是“key”行以 : 结尾的地方,而其他行则没有。

#!/bin/bash
#
# join "The rest of the story" when the first line of each   story
# matches $PATTERN
# Nice for looking for specific changes in bart output
#

PATTERN='*:';
LINEOUT=""
while read line; do
    case $line in
        $PATTERN)
                echo ""
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        "")
                LINEOUT=""
                echo ""
                ;;

        *)      LINEOUT="$LINEOUT $line"
                ;;
    esac        
done

【讨论】:

【参考方案11】:
perl -0pE 's^KEY.*?\K\s+(\d+)$ $1msg;' data.txt > data_merged-lines.txt

-0 吞噬整个文件,而不是逐行读取;pE 使用循环包装代码并打印输出,详情请参阅 http://perldoc.perl.org/perlrun.html;^KEY 匹配 " KEY" 在行的开头,然后在

序列之前进行任何非贪婪匹配 (.*?)
    一个或多个空格\s+,包括换行符; 一个或多个数字 (\d+) 我们捕获并稍后重新插入为 $1

紧随$行的结尾。

\K 方便地将其左侧的所有内容排除在替换之外,因此 $1 仅替换 1-2 序列,请参阅 http://perldoc.perl.org/perlre.html。

【讨论】:

【参考方案12】:

你也可以使用下面的vi命令:

:%g/.*/j

【讨论】:

甚至是:%g//j,因为您只需要匹配要执行的 join,并且空字符串仍然是有效的正则表达式。 @ghoti,在 Vim 中,当仅使用 // 时,将使用之前的搜索模式。如果没有先前的模式,Vim 只是简单地报告一个错误并且什么都不做。 Jdamian 的解决方案一直有效。 @TzunghsingDavidWong - 这对 vim 用户来说是一个很好的指针。对我来说很方便,这个问题和这个答案都没有提到 vim。【参考方案13】:

最简单的方法在这里:

    删除偶数行并将其写入某个临时文件 1。 删除奇数行并将其写入某个临时文件 2。 使用带有-d的粘贴命令将两个文件合二为一(表示删除空格)

sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2

【讨论】:

【参考方案14】:

这是awk的另一种方式:

awk 'ORS=NR%2?FS:RS' file

$ cat file
KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

$ awk 'ORS=NR%2?FS:RS' file
KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

正如 cmets 中的 Ed Morton 所指出的那样,最好添加大括号以确保安全,并添加括号以提高便携性。

awk ' ORS = (NR%2 ? FS : RS)  1' file

ORS 代表输出记录分隔符。我们在这里所做的是使用存储行号的NR 测试条件。如果NR 的模为真值(>0),那么我们将输出字段分隔符设置为FS(字段分隔符)的值,默认为空格,否则我们分配RS 的值(记录分隔符)这是换行符。

如果您希望添加, 作为分隔符,请使用以下内容:

awk ' ORS = (NR%2 ? "," : RS)  1' file

【讨论】:

绝对是正确的方法,所以 +1 但我想知道正在评估什么条件以调用打印记录的默认操作。是不是任务成功了?它只是ORS 并且被视为true,因为ORS 获得的值不是零或空字符串并且awks 正确猜测它应该是一个刺痛而不是数字比较?是别的吗?我真的不确定,所以我会把它写成awk 'ORS=(NR%2?FS:RS)1' file。我也将三元表达式括起来以确保可移植性。 @EdMorton 是的,我刚刚看到有人对这个答案表示赞同,即将对其进行更新以包括安全括号。也会添加括号。【参考方案15】:

"ex" 是一个可编写脚本的行编辑器,它与 sed、awk、grep 等属于同一家族。我想它可能正是您要找的。许多现代 vi 克隆/继承者也有 vi 模式。

 ex -c "%g/KEY/j" -c "wq" data.txt

这表示对于每一行,如果它与“KEY”匹配,则执行下一行的 j oin。在该命令完成后(针对所有行),发出 w 仪式和 q uit。

【讨论】:

【参考方案16】:
nawk '$0 ~ /string$/ printf "%s ",$0; getline; printf "%s\n", $0' filename

这读作

$0 ~ /string$/  ## matches any lines that end with the word string
printf          ## so print the first line without newline
getline         ## get the next line
printf "%s\n"   ## print the whole line and carriage return

【讨论】:

【参考方案17】:

尽管以前的解决方案似乎可行,但如果文档中出现单个异常,则输出将变得支离破碎。下面比较安全。

sed -n '/KEY/
N
s/\n/ /p
' somefile.txt

【讨论】:

为什么更安全? /KEY/ 是做什么的? p 最后做了什么? /KEY/ 搜索带有KEY 的行。 p 打印出结果。它更安全,因为它只在其中包含 KEY 的行上应用操作。【参考方案18】:

这是我在 bash 中的解决方案:

while read line1; do read line2; echo "$line1, $line2"; done < data.txt

【讨论】:

【参考方案19】:

你可以像这样使用 awk 来组合两对线:

awk ' if (NR%2 != 0) line=$0; else printf("%s %s\n", line, $0); line="";  \
     END if (length(line)) print line;' flle

【讨论】:

【参考方案20】:

paste 很适合这份工作:

paste -d " "  - - < filename

【讨论】:

一个关于参数的描述将是一个很好的补充【参考方案21】:

如果 Perl 是一个选项,您可以尝试:

perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt

【讨论】:

-0 是否告诉 perl 将记录分隔符 ($/) 设置为 null,以便我们可以在匹配模式中跨越多行。联机帮助页对我来说有点太技术性了了解它在实践中的含义。

以上是关于如何从命令行将每两行合并为一行?的主要内容,如果未能解决你的问题,请参考以下文章

如何反转文件中的行顺序?

vim常用命令总结

vim命令总结

vim经常使用命令总结

使用 LINQ C# 将两行合并为基于列的单行

cat命令