将列表变成带有分隔符的单行

将列表变成带有分隔符的单行

我必须采用以下格式获取 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但我无法让它工作。

答案1

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

答案2

163MiB我很好奇其中一些(+一些替代方案)如何在相当大的文件(每行一个IP,约 1300 万行)中快速工作:

wc -l < iplist
13144256

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

钢铁起子
非常慢。等待两分钟后中止...所以这个没有结果。

库恩勒姆:

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

麦克塞夫:

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

real    0m0.983s

杰蒂尔:

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

real    0m4.903s

阿维纳什·拉吉:

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

值0x00ff:

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

real    3m4.321s

意思是184.321s。毫不奇怪,这比慢 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 -ple '$\=eof()?"\n":" | "' iplist

real    0m9.646s

参数:

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

答案3

您可以使用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

答案4

所以我整个事情都错了——这个问题教会了我很多关于……的知识paste。正如 cuonglm 正确指出的那样,除非您在串行paste中使用 in 文件-s,否则您总是会\n在写入时将 infile 列表中的最后一个 ewline 附加到输出中。我错误地认为paste -s行为是其默认模式——这是一种误解,显然busybox paste很乐意强化这种误解。以下命令确实按照 w/ 所宣传的那样工作busybox

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

但它不能按照规范工作。正确实现的paste仍然会\n为每个写入的序列附加一个尾随的 ewline。不过,这毕竟没什么大不了的:

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

相关内容