Unix命令行上的简洁便携式“连接”


77

如何将多行合并为一行,并在换行符所在的位置使用分隔符,并避免尾随分隔符,并且可以选择忽略空行?

例。考虑一个foo.txt带有三行的文本文件:

foo
bar
baz

所需的输出是:

foo,bar,baz

我现在使用的命令:

tr '\n' ',' <foo.txt |sed 's/,$//g'

理想情况是这样的:

cat foo.txt |join ,

什么是:

  1. 最便携,简洁,易读的方式。
  2. 使用非标准unix工具的最简洁方法。

我当然可以写点东西,或者只使用别名。但是我很想知道这些选项。


Answers:


130

也许有些令人惊讶,paste是执行此操作的好方法:

paste -s -d","

这不会处理您提到的空行。为此,请先通过管道传递文本grep

grep -v '^$' | paste -s -d"," -

@codaddict也不行,但是我必须承认我根本不觉得它很直观-我总是需要检查手册页。我肯定很好奇其他人的建议。
Michael J. Barber

还有其他方法,但是没有更好的方法了(有趣的方法有点笨拙)。
sorpigal 2011年

它似乎并没有忽略空行,但这仍然非常好,并且适用于我的用例。谢谢!
屁股

13
为了增强可移植性,请-paste希望从中读取任何内容时考虑在命令末尾添加stdin。(某些pasteBSD的版本,例如BSD,stdin除非-明确传递给它,否则不会读取。)
kjo 2013年

2
感谢您的提示paste!我注意到它仅允许使用单字符定界符,并且\t默认情况下。为了实现更长的分隔符(如, ):cat foo.txt | paste -s | sed 's/\t/, /g'
Arild

12

这一sed行应该可以-

sed -e :a -e 'N;s/\n/,/;ba' file

测试:

[jaypal:~/Temp] cat file
foo
bar
baz

[jaypal:~/Temp] sed -e :a -e 'N;s/\n/,/;ba' file
foo,bar,baz

要处理空行,您可以删除空行并将其通过管道输送到上述一线管。

sed -e '/^$/d' file | sed -e :a -e 'N;s/\n/,/;ba'

一个解释会很好!
特哈斯·羽衣甘蓝

1
将2 -e表达式组合为1更清楚sed -e ':a; N; s/\n/,/; ba'。但这仍然是O(n²)方法,因为sed每次添加新行时都会做一些替换。sed -e ':a; N; $!ba; s/\n/,/g'是线性的,仅在将所有行添加到sed的模式空间后才替换一次。$!ba表示“如果这是最后一行($),请不要(!)跳转到(b)标签:a(a),打破循环”
zhazha

8

如何使用xargs?

对于你的情况

$ cat foo.txt | sed 's/$/, /' | xargs

请注意xargs命令的输入限制长度。(这意味着很长的输入文件不能由此处理。)


我发现-L xargs上的标志-L 50会帮助每行50个项目。
jmunsch '16

6

Perl:

cat data.txt | perl -pe 'if(!eof){chomp;$_.=","}'

或更短,更快,令人惊讶的是:

cat data.txt | perl -pe 'if(!eof){s/\n/,/}'

或者,如果您想:

cat data.txt | perl -pe 's/\n/,/ unless eof'

2
这样做的好处是您可以使用任何字符串,而不仅仅是简单的逗号。公认的答案通用性较低。我特别喜欢最后的迭代,尽管我会这样写:(perl -pe 's/\n/,/ unless eof' data.txt 不需要伪造的猫)。
Mike S

4

只是为了好玩,这是一个全内置的解决方案

IFS=$'\n' read -r -d '' -a data < foo.txt ; ( IFS=, ; echo "${data[*]}" ; )

如果尾随换行符有问题,可以使用printf代替echo

这是通过将IFSread将要分割的定界符设置为换行符而不是其他空格,然后告诉read直到达到anul而不是它通常使用的换行符来停止读取,并将读取的每个项添加到数组中的(-a)数据。然后,在子IFS外壳程序中,为了不破坏交互式外壳程序的内容,我们将设置IFS为,,并用展开该数组*,该数组用的第一个字符界定了数组中的每个项目IFS


1
有趣,但是可移植性并不出色,因为-dshshellread命令中没有选项。
mykhal 2014年

@mykhal:是的。但是,bash可以在许多系统上找到,因此它具有一定的实用性。如果您也希望可移植性阵列也可以使用,否则您可以简单地使用while循环来解决缺少的问题-d。对于一个适当的,可移植的全内置版本,您想要类似的东西,但是知道c= ; while IFS= read -r d ; do if ! [ -z "$d" ] ; then printf "$c$d" ; fi c=, ; done < foo.txt仍然会失败,但是可以省略,并假定为内置版本,因此如果效率很重要,那可能会更好。不过,可接受的答案要好得多!read-rprintfecho
sorpigal

0

我需要完成类似的工作,从文件中打印以逗号分隔的字段列表,并且对将STDOUT传递到xargs和感到满意ruby,如下所示:

cat data.txt | cut -f 16 -d ' ' | grep -o "\d\+" | xargs ruby -e "puts ARGV.join(', ')"

0

我有一个日志文件,其中一些数据分为多行。发生这种情况时,第一行的最后一个字符是分号(;)。我使用以下命令加入了这些行:

for LINE in 'cat $FILE | tr -s " " "|"'
do
    if [ $(echo $LINE | egrep ";$") ]
    then
        echo "$LINE\c" | tr -s "|" " " >> $MYFILE
    else
        echo "$LINE" | tr -s "|" " " >> $MYFILE
    fi
done

结果是一个文件,其中在日志文件中拆分的行与我的新文件中的一行相同。


0

使用ex(就可以忽略空白行)使用就地空格连接线的简单方法是:

ex +%j -cwq foo.txt

如果要将结果打印到标准输出,请尝试:

ex +%j +%p -scq! foo.txt

要连接没有空格的行,请使用+%j!代替+%j

要使用不同的定界符,会比较棘手:

ex +"g/^$/d" +"%s/\n/_/e" +%p -scq! foo.txt

其中g/^$/d(或v/\S/d)删除空行,并且s/\n/_/是替代行,基本上与使用相同sed,但对于所有行(%)。解析完成后,打印缓冲区(%p)。最后-cq!执行viq!命令,该命令基本上不保存-s就退出(使输出静音)。

请注意,这ex等效于vi -e

由于大多数Linux / Unix默认附带ex/ vi,因此该方法可移植性很强。而且sed,与就地参数(-i)不是标准扩展而实用程序本身更面向流的情况相比,它更具兼容性,因此它不是那么可移植。


-1

我的答案是:

awk '{printf "%s", ","$0}' foo.txt

printf足够。我们不需要-F"\n"更改字段分隔符。


1
这会在输出的开头添加一个虚假的逗号。-1为未测试。
Mike S
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.