用定界符将列表变成单行


17

我必须以这种格式列出(负载)IP地址列表:

 134.27.128.0
 111.245.48.0
 109.21.244.0

并通过中间的管道将它们转换为这种格式(组成IP)

134.27.128.0 | 111.245.48.0 | 109.21.244.0 | 103.22.200.0/22

我认为这是一个查找和替换命令,sed但我无法使其正常工作。


3
您只想将tr换行符添加到|管道中?喜欢<ipfile tr \\n \| >outfile吗?
mikeserv

是否|需要周围空间?
cuonglm

2
@uselesslinuxman-不。您需要输入重定向<。所以<mydoc tr \\n \| >mydoc2。但这不会给您空间。对于那些人,最快的解决方案可能是paste -d' | ' mydoc /dev/null /dev/null >mydoc2
mikeserv 2015年

1
@mikeserv:我认为它不会起作用。paste写入每个文件对应的行。没有-s,您将获得文件中的行数。
cuonglm

2
@ val0x00ff:我邀请您阅读unix.stackexchange.com/q/169716/38906
cuonglm

Answers:


16

使用sed的基础上著名的sed单行解释,第一部分:39追加一行到下一个,如果它用反斜杠“\”结尾(除了在这里我们忽略有关反斜杠的一部分,并更换\n与换行必需的|分隔符):

sed -e :a -e '$!N; s/\n/ | /; ta' mydoc > mydoc2

应该在 mydoc2

134.27.128.0 |  111.245.48.0 |  109.21.244.0

@don_crissti很抱歉,这是一种类型-已更正,谢谢
steeldriver 2015年

不幸的是,这实际上在实践中不起作用。至少,不是无限流。执行此操作时,您必须一次吞下整个输入一行,并且在消化完所有内容之前,甚至无法写入其中的单个字节以将其全部输出-所有内容都转换为一行。它笨拙并且容易出现段错误。
mikeserv

一百万个IP小于1600万,您需要一个庞大的清单才能突破限制。使用搜索进行eof检测更加麻烦,因为这将在输入文件大小上运行O(N ^ 2)。 sed 'H;1h;$!d;x;s/\n/ | /g'是线性的。
jthill

@jthill-POSIX仅保证sed8K 的模式空间;少于1600万。
mikeserv

9

我很想知道其中的一些(以及一些替代方法)如何在一个相当大的文件(163MiBIP每行一个,〜1300万行)下快速运行:

wc -l < iplist
13144256

结果(sync; echo 3 > /proc/sys/vm/drop_caches在每个命令之后;在几个小时之后,我以相反的顺序重复了测试,但是差异可以忽略不计;还请注意,我正在使用gnu sed):

steeldriver
非常慢。经过两分钟的等待后中止了...所以没有结果。

cuonglm

awk 'FNR!=1{print l}{l=$0};END{ORS="";print l}' ORS=' | ' iplist

real    0m3.672s

perl -pe 's/\n/ | / unless eof' iplist

real    0m12.444s

mikeserv

paste -d\  /dev/null iplist /dev/null | paste -sd\| - 

real    0m0.983s

jthill

sed 'H;1h;$!d;x;s/\n/ | /g' iplist

real    0m4.903s

阿维纳什·拉吉Avinash Raj)

time python2.7 -c'
import sys
with open(sys.argv[1]) as f:
    print " | ".join(line.strip() for line in f)' iplist

real    0m3.434s

val0x00ff

while read -r ip; do printf '%s | ' "$ip"; done < iplist

real    3m4.321s

这意味着184.321s。毫不奇怪,这比mikeserv的解决方案慢200倍。


这是使用
awk的其他一些方法:

awk '$1=$1' RS= OFS=' | ' iplist

real    0m4.543s

awk '{printf "%s%s",sep,$0,sep=" | "} END {print ""}' iplist

real    0m5.511s

perl:

perl -ple '$\=eof()?"\n":" | "' iplist

real    0m9.646s

xargs:

xargs <iplist printf ' | %s' | cut -c4-

real    0m6.326s

head + paste + tr + cat的组合:

{ head -n -1 | paste -d' |' - /dev/null /dev/null | tr \\n \ ; cat ; } <iplist

real    0m0.991s

如果您有GNU coreutils,并且您的IP列表不是很庞大(比如说最多50000个IP),也可以使用以下方法pr

pr -$(wc -l infile) -tJS' | ' -W1000000 infile >outfile

哪里

-$(wc -l infile)         # no. of columns (= with no. of lines in your file)
-t                       # omit page headers and trailers
-J                       # merge lines
-S' | '                  # separate columns by STRING
-W1000000                # set page width

例如6行文件:

134.28.128.0
111.245.28.0
109.245.24.0
128.27.88.0
122.245.48.0
103.44.204.0

命令:

pr -$(wc -l <infile) -tJS' | ' -W1000 infile

输出:

134.28.128.0 | 111.245.28.0 | 109.245.24.0 | 128.27.88.0 | 122.245.48.0 | 103.44.204.0

唐-您还可以在问题中通过@ val0x00ff添加建议的while ... read循环吗?我很好奇在基准测试中看到163k read()write()调用的含义。好的答案,顺便说一句。
mikeserv

1
@mikeserv-没问题,我会做的(尽管那确实很慢)。
don_crissti 2015年

这是一个非常酷的链接。我特别喜欢作者也提供了一个类似的6年基准测试的链接。您是否注意到那段时间sed似乎提高了它的地位(并且可能仅对其正则表达式引擎进行了很少的更改),grep似乎在性能上却大大落后了(尤其是较长的行)?我不知道perl其引擎的增加是否会对这些结果产生影响……整洁dash也并非很糟糕bashw /与常见的IFS=前缀相比,这里的速度可能要慢得多。
mikeserv

嗯...该链接是我确实需要扎根并学习C的另一个有力指标,因此我终于可以开始lex正确使用。
mikeserv

8

您可以使用awk

awk 'FNR!=1{print l}{l=$0};END{ORS="";print l}' ORS=' | ' file > new_file

ORS=' | '输出记录分隔符设置为' | '而不是换行符。

或使用进行就地编辑perl

perl -pe 's/\n/ | / unless eof' file

谢啦。我刚刚学会了paste工作原理。非常感激。
mikeserv

@mikeserv:不客气。如don_crissti在其基准测试中所示,该paste解决方案是最快的解决方案。
cuonglm

输出不以换行符结尾。您可能需要用替换ORS=""END块内的ORS="\n"内容。
phk

4

所以我觉得整个事情都是错误的-这个问题教会了我很多知识paste。正如cuonglm正确指出的那样,除非您paste以erial格式输入文件,否则在写入文件时-s,总是将文件目录中的最后一条\n尾线附加到输出中。我误以为paste -s行为是其默认模式,这是一种误解,这是一个错误观念,显然busybox paste很高兴予以强化。以下命令的确可以通过w /进行宣传busybox

paste -d'|  ' - - infile </dev/null >outfile

但是,根据规范它不起作用。正确实施的paste方法仍将\n为每个写入的序列附加尾随的ewline。毕竟,这没什么大不了的:

paste -d\  - infile - </dev/null | paste -sd\| - >outfile

@don_crissti-丹吉特 愚蠢的平板电脑。我想要做的显而易见的事情是两贴。
mikeserv

1
好吧,我已经pr记过了,但是显然它在输入文件很大的情况下用尽了,所以我无法实际测试速度,但是使用合理的长度文件就可以了。您的解决方案是迄今为止最快的(毫不奇怪- paste确实非常快),请参阅我的文章。
don_crissti 2015年

4

带有tr和sed的单线:

cat file | tr '\n' '|' | sed 's/||$/\n/'
134.27.128.0|111.245.48.0|109.21.244.0

为什么要删除2条尾随管道?如果输入以空白行(两个换行符)结尾,则末尾将只有2个。
JigglyNaga

3

利用vim

vim -n -u NONE -c '1,$-1s/\n/ | /g|wq!' data

说明:

-n 禁用交换文件

-u NONE 用于跳过所有初始化。

-c {command} 读取文件后执行命令。

1,$-1s/\n/ | /gs/\n/ | /g范围1,$-1s(第一行到最后一行-1)(用空间管道空间替换换行符)

wq! 强制写入并退出


注意:

根据文件的大小,这可能不是一个好主意。


1
我感谢大家,因为基本上这些命令中的几乎每一个都能满足我需要实现的目标。如果(当)我再次被困住,我知道现在要去哪里。谢谢
uselesslinuxman 2015年


2

这是另一个使用 xxd

xxd -c1 -ps data | sed '$!s/0a/207c20/' | xxd -r -ps

2

为了完整起见,这是另一种awk基于解决方案的解决方案,该解决方案根本不使用ORS

awk 'BEGIN { ORS="" } { print p$0; p=" | " } END { print "\n" }' file > new_file

有关说明,请参阅我的帖子,网址/unix//a/338121/117599

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.