Como gerar o arquivo com toda a string que corresponde ao padrão de outro arquivo

1

Eu tenho um arquivo assim:

Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \

Eu quero gerar um arquivo assim:

Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h

Como fazer isso usando Bash / Sed / Awk / Grep ou algo assim?

    
por marcin 03.10.2016 / 16:38

2 respostas

2

Se você tem um Awk que suporta expressões regulares para o separador de registros RS , isso pode ser feito assim:

awk 'BEGIN { RS = " +| *\\?\n" } 1'

A vantagem disso é que não estamos armazenando o arquivo inteiro na memória e fazendo alguma substituição de regex; sua entrada pode ter gigabytes por muito tempo.

Basicamente, tratamos o arquivo como tendo dois separadores de registro: um ou mais espaços, ou então zero ou mais espaços seguidos por uma nova linha, que pode ser precedida por uma barra invertida opcional.

Tendo delimitado os registros desta forma, tudo o que precisamos fazer é imprimi-los, seguido pelo separador de registro de saída padrão ( ORS ), que, obviamente, é nova linha. Isso é obtido por uma regra de ação padrão que consiste em 1 .

Ou uma tarefa de pipeline com sed e tr , não usando nada que não esteja em POSIX:

tr '\n' ' ' | sed -e 's/\//g' -e 's/ \+/ /g' | tr ' ' '\n'

Substitua novas linhas por espaços. Em seguida, a squash é executada em vários espaços em um espaço, removendo as barras invertidas. Em seguida, mapeie espaços para novas linhas.

    
por 03.10.2016 / 17:01
2

Com o GNU grep

$ cat file 
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \

$ grep -o '[^\ ]*' file 
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
  • -o extrai apenas o padrão de correspondência
  • [^\ ]* zero ou mais de caracteres não espaciais e não \ , pois * é ganancioso, tentará corresponder o máximo de caracteres possíveis

para salvar o resultado em outro arquivo, use

$ grep -o '[^\ ]*' file > out_file


Como apontado por @ Stéphane Chazelas, é melhor usar o seguinte para ser mais portátil:

grep -oE '[^\ ]+' file

em que -E chama a regex estendida e [^\ ]+ corresponde a um ou mais caracteres não espaciais e não \


Análise de desempenho:

$ perl -ne 'print "$_"x100000' file > file_big
$ shuf file_big -o file_big 

$ du -sh file_big 
9.0M    file_big

Todas as respostas e sugestões dos comentários usados para comparação:

$ time grep -o '[^\ ]*' file_big > o1

real    0m2.090s
user    0m2.076s
sys 0m0.016s

$ time grep -oE '[^\ ]+' file_big > o2

real    0m1.523s
user    0m1.504s
sys 0m0.012s

$ time awk 'BEGIN { RS = " +| *\\?\n" } 1' file_big > o3

real    0m0.331s
user    0m0.320s
sys 0m0.008s

$ time tr -s '\ ' '[\n*]' < file_big | grep . > o4

real    0m0.095s
user    0m0.124s
sys 0m0.008s

$ time tr '\ ' '[\n*]' < file_big | grep . > o5

real    0m0.105s
user    0m0.104s
sys 0m0.016s

Verificação de sanidade

$ diff -s o1 o2
Files o1 and o2 are identical
$ diff -s o1 o3
Files o1 and o3 are identical
$ diff -s o1 o4
Files o1 and o4 are identical
$ diff -s o1 o5
Files o1 and o5 are identical
    
por 03.10.2016 / 16:54