我有一个具有以下格式的文本文件。第一行是“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
如何将两条线合并为一条?
paste
适合这项工作:
paste -d " " - - < filename
awk:
awk 'NR%2{printf "%s ",$0;next;}1' yourFile
请注意,输出末尾有一个空行。
赛德:
sed 'N;s/\n/ /' yourFile
sed、awk、grep 的替代方案:
xargs -n2 -d'\n'
当您想要加入 N 行并且只需要以空格分隔的输出时,这是最好的选择。
我最初的答案是xargs -n2
,它以单词而不是行分隔。 -d
(GNU xargs 选项)可用于按任何单数字符分割输入。
杀死狗的方法比绞死更多。 [1]
awk '{key=$0; getline; print key ", " $0;}'
将您喜欢的任何分隔符放在引号内。
参考:
最初是“给猫剥皮的多种方法”,恢复为更古老的、可能起源于与宠物无关的表达方式。
这是我在 bash 中的解决方案:
while read line1; do read line2; echo "$line1, $line2"; done < data.txt
这是 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
正如评论中的 Ed Morton 所示,最好添加大括号以确保安全,并添加括号以实现可移植性。
awk '{ ORS = (NR%2 ? FS : RS) } 1' file
ORS
代表输出记录分隔符。我们在这里所做的是使用存储行号的 NR
测试条件。如果 NR
的模数是真值(>0),那么我们将输出字段分隔符设置为 FS
(字段分隔符)的值,默认情况下是空格,否则我们分配 RS
的值(记录分隔符)这是换行符。
如果您希望添加 ,
作为分隔符,请使用以下内容:
awk '{ ORS = (NR%2 ? "," : RS) } 1' file
ORS
并且被视为 true
,因为 ORS 获得的值不是零或空字符串,并且 awks 正确猜测它应该是一个字符串而不是数字比较?是别的吗?我真的不确定,所以我会把它写成 awk '{ORS=(NR%2?FS:RS)}1' file
。我也将三元表达式括起来以确保可移植性。
尽管以前的解决方案似乎可行,但如果文档中出现单个异常,则输出将变得支离破碎。下面稍微安全一点。
sed -n '/KEY/{
N
s/\n/ /p
}' somefile.txt
/KEY/
有什么作用? p
最后做了什么?
/KEY/
搜索带有 KEY
的行。 p
将结果打印出来。它更安全,因为它只在其中包含 KEY
的行上应用操作。
使用 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
“ex”是一个可编写脚本的行编辑器,与 sed、awk、grep 等在同一个系列中。我认为它可能是您正在寻找的。许多现代 vi 克隆/继承者也有 vi 模式。
ex -c "%g/KEY/j" -c "wq" data.txt
这表示对于每一行,如果它与“KEY”匹配,则执行下一行的加入。在该命令完成后(针对所有行),发出 aw rite 并退出。
您可以像这样使用 awk 来组合两行:
awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \
END {if (length(line)) print line;}' flle
使用 vim 的另一种解决方案(仅供参考)。
解决方案1:
在 vim vim filename
中打开文件,然后执行命令 :% normal Jj
这个命令很容易理解:
% :对于所有行,
normal : 执行普通命令
Jj : 执行 Join 命令,然后跳转到下一行
之后,保存文件并使用 :wq
退出
解决方案2:
在 shell 中执行命令 vim -c ":% normal Jj" filename
,然后保存文件并使用 :wq
退出。
J
的情况下,norm!
也比 normal
更健壮。 +1 用于 vim 解决方案。
如果 Perl 是一个选项,您可以尝试:
perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt
-0
是否告诉 perl 将记录分隔符 ($/)
设置为 null,以便我们可以在匹配模式中跨越多行。联机帮助页对我来说有点过于技术性,无法弄清楚它在实践中的含义。
您还可以使用以下 vi 命令:
:%g/.*/j
:%g//j
,因为您只需要匹配要执行的 join,并且空字符串仍然是有效的正则表达式。
//
时,将使用以前的搜索模式。如果没有先前的模式,Vim 只是简单地报告一个错误并且什么都不做。 Jdamian 的解决方案一直有效。
使用 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
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
在我需要合并两行(以便于处理)但允许数据超过特定的情况下,我发现这很有用
数据.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
输出看起来像:
转换数据.txt
string1=x string2=y
string3
string4
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
最简单的方法在这里:
删除偶数行并将其写入某个临时文件 1. 删除奇数行并将其写入某个临时文件 2. 使用带有 -d 的粘贴命令将两个文件合二为一(表示删除空间)
sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2
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”行首,后面是任何非贪婪匹配(.*?
)
一个或多个空格 \s+ 任何类型的空格,包括换行符;我们捕获并随后重新插入为 $1 的一个或多个数字 (\d+);
后跟第 $
行的结尾。
\K
方便地将其左侧的所有内容排除在替换之外,因此 { $1}
仅替换 1-2 序列,请参阅 http://perldoc.perl.org/perlre.html。
一种更通用的解决方案(允许加入多个后续行)作为 shell 脚本。这在每个之间添加了一条线,因为我需要可见性,但这很容易解决。此示例是“关键”行以 : 结尾的地方,而其他行则没有。
#!/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
尝试以下行:
while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file
将分隔符放在中间
"$line1 $line2";
例如,如果分隔符是 |
,则:
"$line1|$line2";
paste
总是从每个输入文件 中获取一行,并输出一行,这些行由用-d
定义的分隔符组合而成。这里的诀窍是列出魔法输入文件-
两次。按照 GNU 的传统,文件名-
表示从标准输入中读取,并且为paste
列出该魔法文件两次意味着它将从“第一个”输入文件中读取一行,然后从“第二个”输入文件中读取一行,总共 2从标准输入读取的行。然后将这些行与它们之间的单个空格连接起来。请注意,-d
只占用一个字节。