ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一个具有以下格式的文本文件。第一行是“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! (xargsbash 慢!)

g
glenn jackman

paste 适合这项工作:

paste -d " "  - - < filename

关于参数的描述将是一个很好的补充
这是因为 paste 总是从每个输入文件 中获取一行,并输出一行,这些行由用 -d 定义的分隔符组合而成。这里的诀窍是列出魔法输入文件 - 两次。按照 GNU 的传统,文件名 - 表示从标准输入中读取,并且为 paste 列出该魔法文件两次意味着它将从“第一个”输入文件中读取一行,然后从“第二个”输入文件中读取一行,总共 2从标准输入读取的行。然后将这些行与它们之间的单个空格连接起来。请注意,-d 只占用一个字节。
K
Kent

awk:

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

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

赛德:

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

n
nnog

sed、awk、grep 的替代方案:

xargs -n2 -d'\n'

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

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


C
Community

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

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

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

参考:

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


H
Hai Vu

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

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

C
Community

这是 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

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

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

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

为什么更安全? /KEY/ 有什么作用? p 最后做了什么?
/KEY/ 搜索带有 KEY 的行。 p 将结果打印出来。它更安全,因为它只在其中包含 KEY 的行上应用操作。
B
Benjamin W.

使用 pasteglenn 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

J
Justin

“ex”是一个可编写脚本的行编辑器,与 sed、awk、grep 等在同一个系列中。我认为它可能是您正在寻找的。许多现代 vi 克隆/继承者也有 vi 模式。

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

这表示对于每一行,如果它与“KEY”匹配,则执行下一行的加入。在该命令完成后(针对所有行),发出 aw rite 并退出。


a
anubhava

您可以像这样使用 awk 来组合两行:

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

J
Jensen

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

解决方案1:

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

这个命令很容易理解:

% :对于所有行,

normal : 执行普通命令

Jj : 执行 Join 命令,然后跳转到下一行

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

解决方案2:

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


在重新映射 J 的情况下,norm! 也比 normal 更健壮。 +1 用于 vim 解决方案。
@qeatzy 谢谢你教我。很高兴知道。 ^_^
a
andrefs

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

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

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

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

:%g/.*/j

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

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

N
New Alexandria
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

B
Benjamin W.

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

数据.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

W
Weihang Jian
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

j
jaypal singh

最简单的方法在这里:

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

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

O
Onlyjob
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


J
Jan Parcel

一种更通用的解决方案(允许加入多个后续行)作为 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

c
coatless

尝试以下行:

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

将分隔符放在中间

"$line1 $line2";

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

"$line1|$line2";

此答案未添加 Hai Vu's answer 中未提供的任何内容,但在您之前 4 年发布。
我部分同意,我尝试添加解释和更通用的它也不会编辑旧文件。谢谢你的建议