Transforme a lista em uma única linha com delimitador

12

Eu tenho que pegar uma lista (carrega) de endereços IP neste formato:

 134.27.128.0
 111.245.48.0
 109.21.244.0

e transformá-los nesse formato com um pipe intermediário (IPs criados)

134.27.128.0 | 111.245.48.0 | 109.21.244.0 | 103.22.200.0/22

Acho que é um comando localizar e substituir como sed , mas não consigo fazer isso funcionar.

    
por uselesslinuxman 01.04.2015 / 19:20

9 respostas

12

Usando o sed, baseado em Famous Sed-liners Explained, Parte I: : 39. Anexe uma linha à próxima se ela terminar com uma barra invertida "\" (exceto aqui ignoramos a parte sobre a barra invertida e substituímos as \n newlines pelo | separador necessário):

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

deve produzir em mydoc2

134.27.128.0 |  111.245.48.0 |  109.21.244.0
    
por 01.04.2015 / 19:46
8

Você pode usar o awk :

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

ORS=' | ' define o separador de registros de saída como ' | ' em vez de novas linhas .

ou edite no local com perl :

perl -pe 's/\n/ | / unless eof' file
    
por 01.04.2015 / 19:38
8

Eu estava curioso para ver como algumas delas (+ algumas alternativas) funcionam em velocidade com um arquivo bastante grande ( 163MiB , um IP por linha, ~ 13 milhões de linhas):

wc -l < iplist
13144256

Resultados (com sync; echo 3 > /proc/sys/vm/drop_caches após cada comando; Repeti os testes - em ordem inversa - depois de algumas horas, mas as diferenças foram insignificantes; observe também que estou usando gnu sed ):

steeldriver :
Muito devagar. Abortado após dois minutos de espera ... então não há resultado para este.

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

e

val0x00ff :

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

real    3m4.321s

que significa 184.321s . Sem surpresa, isso é 200 vezes mais lento que a solução do mikeserv .

Aqui estão algumas outras maneiras com as quais 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

uma combinação de cabeçalho + colar + tr + cat:

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

real    0m0.991s

Se você tem GNU coreutils e sua lista de IPs não é muito grande (digamos até 50000 IPs), você também pode fazer isso com pr :

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

onde

-$(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

por exemplo. para um arquivo de 6 linhas:

134.28.128.0
111.245.28.0
109.245.24.0
128.27.88.0
122.245.48.0
103.44.204.0

o comando:

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

saídas:

134.28.128.0 | 111.245.28.0 | 109.245.24.0 | 128.27.88.0 | 122.245.48.0 | 103.44.204.0
    
por 02.04.2015 / 14:29
4

Então eu tive tudo errado - e essa questão me ensinou muito sobre paste . Como o cuonglm observa corretamente, a menos que você paste e no arquivo -s , você sempre acabará com a última \n ewline da sua lista de arquivos sendo anexada à saída como está escrita. Eu estava enganado na crença de que paste -s behavior era seu modo padrão - e este é um equívoco que, aparentemente busybox paste estava feliz em reforçar. O comando a seguir funciona como anunciado com busybox :

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

Não funciona de acordo com as especificações. Um paste implementado corretamente ainda acrescentaria um ewline \n final para cada sequência gravada. Ainda assim, isso não é grande coisa afinal:

paste -d\  - infile - </dev/null | paste -sd\| - >outfile
    
por 01.04.2015 / 20:40
3

Utilize vim :

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

Explicação:

-n desativar o arquivo de troca

-u NONE é usado para pular todas as inicializações.

-c {command} executa comandos depois que o arquivo foi lido.

1,$-1s/\n/ | /g is s/\n/ | /g (substitua a nova linha pelo pipe espacial    espaço) para o intervalo 1,$-1s (1ª linha até a última linha - 1)

wq! force escrever e sair

Nota:

Dependendo do tamanho do seu arquivo, isso pode ser uma má ideia.

    
por 01.04.2015 / 22:41
2

Através do python.

$ python -c '
import sys
with open(sys.argv[1]) as f:
    print " | ".join(line.strip() for line in f)' file

espaços antes de print ser muito importante.

    
por 02.04.2015 / 05:13
2

Aqui está outro usando xxd

xxd -c1 -ps data | sed '$!s/0a/207c20/' | xxd -r -ps
    
por 02.04.2015 / 08:00
2

Por razões de integralidade, aqui está outra solução baseada em awk , esta não está usando ORS :

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

Para obter uma explicação, veja minha postagem no link .

    
por 17.01.2017 / 20:29
1

one-liner com tr e sed:

cat file | tr '\n' '|' | sed 's/||$/\n/'
134.27.128.0|111.245.48.0|109.21.244.0
    
por 19.01.2017 / 18:35