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

我有一个如下格式的文本文件。第一行是“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

我如何将两条线合并成一条?

294828 次浏览

你可以像这样使用awk来合并2对行:

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

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

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

paste适合这个工作:

paste -d " "  - - < filename

awk:

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

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

对话:

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

杀死一只狗的方法不止绞刑。<一口>[1]< /一口>

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

在引号内放入您喜欢的任何分隔符。


引用:

  1. 原本是“Plenty of ways to skin the cat”,后来变成了一个古老的、可能起源于宠物的表达,也和宠物无关。

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

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

虽然前面的解决方案似乎可以工作,但如果文档中出现一个异常,输出就会支离破碎。下面稍微安全一点。

sed -n '/KEY/{
N
s/\n/ /p
}' somefile.txt
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

下面是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

正如注释中的埃德莫顿所指出的,最好为安全性添加大括号,为可移植性添加小括号。

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

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

如果你想添加,作为分隔符,那么使用以下方法:

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

"ex"是一个可脚本化的行编辑器,与sed、awk、grep等属于同一家族。我觉得这可能就是你要找的东西。许多现代vi克隆/后继者也有一个vi模式。

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

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

最简单的方法是:

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

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

您也可以使用以下vi命令:

:%g/.*/j
perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt

-0狼吞虎咽整个文件,而不是逐行读取 pE用循环包装代码并打印输出,详见http://perldoc.perl.org/perlrun.html;
^KEY匹配行首的"KEY",后面是

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

后跟$行末尾。

\K方便地排除左边的所有替换,因此{ $1}只替换1-2个序列,参见http://perldoc.perl.org/perlre.html

更通用的解决方案(允许连接多个后续行)是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

在我需要合并两行(为了更容易处理),但允许数据超过特定的情况下,我发现这是有用的

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

sed、awk、grep的替代方案:

xargs -n2 -d'\n'

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

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

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

解决方案1:

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

这个命令很容易理解:

  • %:对于所有的行,
  • Normal:执行正常命令
  • 京晶:执行Join命令,然后跳转到线下

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

解决方案2:

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

试试下面这句话:

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

在中间放置分隔符

"$line1 $line2";

例如,如果分隔符是|,则:

"$line1|$line2";

使用paste格伦·杰克曼的回答略有变化:如果-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

使用vim的另一种方法是:

:g/KEY/join

这将对所有包含KEY的行应用join(对它下面的行)。结果:

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
cat input.txt
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 -sd ' \n' input.txt
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 -sd ' \n' input.txt | rev | sed 's/ / , /' | rev
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