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

时间:2012-03-07 16:19:57

标签: bash awk sed grep

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

如何将两行合并为一个?

21 个答案:

答案 0 :(得分:204)

paste对这项工作有好处:

paste -d " "  - - < filename

答案 1 :(得分:143)

AWK:

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

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

sed的:

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

答案 2 :(得分:29)

替代sed,awk,grep:

xargs -n2 -d'\n'

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

我的原始答案是xargs -n2,它在单词而不是行上分隔。 -d可用于按任何单个字符拆分输入。

答案 3 :(得分:25)

杀狗的方法多于悬挂。 [1]

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

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


参考文献:

  1. 最初“有很多方法可以给猫皮肤”,恢复到一种较老的,可能与宠物无关的表达方式。

答案 4 :(得分:10)

以下是我在bash中的解决方案:

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

答案 5 :(得分:10)

虽然看起来先前的解决方案可行,但如果文档中出现单个异常,则输出将分段。下面有点安全。

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

答案 6 :(得分:10)

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

答案 7 :(得分:7)

&#34;前&#34;是一个可编写脚本的行编辑器,与sed,awk,grep等属于同一个族。我认为它可能就是你要找的东西。许多现代vi克隆/后继者也有vi模式。

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

对于每一行,如果匹配&#34; KEY&#34;执行以下行的 j 。在该命令完成后(针对所有行),发出 w 仪式和 q uit。

答案 8 :(得分:4)

如果选择Perl,您可以尝试:

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

答案 9 :(得分:4)

您可以使用这样的awk来组合2对线:

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

答案 10 :(得分:3)

使用def find_images_with_keyword(key_dict, keywords_list): new_list = [] for item in key_dict.items(): key, filenames = item if key in keywords_list: for filename in filenames: if filename not in new_list: # add this new_list.append(filename) return new_list glenn jackman's answer略有不同:如果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 符合POSIX,则不会修改文件中最后一行的换行符,因此对于具有奇数行的输入文件,例如

paste

KEY 4048:1736 string 3 KEY 0:1772 string 不会对最后一行的分离字符进行处理:

paste

答案 11 :(得分:3)

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

:%g/.*/j

答案 12 :(得分:1)

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

答案 13 :(得分:1)

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

解决方案1 ​​

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

这个命令很容易理解:

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

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

解决方案2

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

答案 14 :(得分:1)

如果我需要组合两行(为了便于处理),但允许数据超过特定的,我发现这是有用的

<强> 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

答案 15 :(得分: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

答案 16 :(得分:0)

更通用的解决方案(允许连接多个后续行)作为shell脚本。这会在每个之间添加一条线,因为我需要可见性,但这很容易解决。这个例子是&#34;键&#34;行结束于:没有其他行。

#!/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

答案 17 :(得分:0)

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

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

序列之前的任何事物(.*?)的非贪婪匹配
  1. 任意类型的一个或多个空格\s+,包括换行符;
  2. 我们捕获并稍后重新插入(\d+)的一个或多个数字$1;
  3. 后跟行$的结尾。

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

答案 18 :(得分:0)

最简单的方法是:

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

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

答案 19 :(得分:-1)

请尝试以下行:

<div class="ui secondary pointing menu">
  <a class="active item">
    Home
  </a>
  <a class="item">
    Messages
  </a>
  <a class="item">
    Friends
  </a>
  <div class="right menu">
    <a class="ui item">
      Logout
    </a>
  </div>
</div>
<div class="ui segment">
  <p></p>
</div>

将分隔符置于

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

e.g。如果分隔符是"$line1 $line2"; ,那么:

|

答案 20 :(得分:-2)

您可以像这样使用xargs

xargs -a file