Como você pode combinar todas as linhas que terminam com um caractere de barra invertida?

36

Usando uma ferramenta de linha de comando comum como sed ou awk, é possível juntar todas as linhas que terminam com um determinado caractere, como uma barra invertida?

Por exemplo, dado o arquivo:

foo bar \
bash \
baz
dude \
happy

Gostaria de obter esta saída:

foo bar bash baz
dude happy
Cory Klein
fonte
11
Passe o arquivo através de cpp:)
imz - Ivan Zakharyaschev
Tantas respostas maravilhosas, eu gostaria de poder marcar todas elas como a resposta! Obrigado pelo excelente visual do awk, sed e perl, esses foram ótimos exemplos.
Cory Klein
Observe que está no sedFAQ
Stéphane Chazelas

Respostas:

27

uma solução sed mais curta e mais simples:

sed  '
: again
/\\$/ {
    N
    s/\\\n//
    t again
}
' textfile

ou one-liner se estiver usando o GNU sed:

sed ':x; /\\$/ { N; s/\\\n//; tx }' textfile
neurino
fonte
11
bom ... Eu olhei inicialmente para isso e não consegui entender (por isso não estava na cesta muito difícil) ... mas depois de uma análise aprofundada da resposta de Gilles (que demorou bastante tempo) Eu dei uma outra olhada na sua resposta e parecia notavelmente compreensível. Acho que estou começando a entender sed:) ... Você está anexando cada linha diretamente ao espaço do padrão e, quando uma linha "normalmente terminada" aparece, o todo o espaço do padrão cai e é impresso automaticamente (porque não existe a opção -n) ... limpo! .. +1
Peter.O
@fred: obrigado, acho que estou começando a entender o sed também, ele oferece boas ferramentas para edição em
várias linhas
Cuidado com as terminações de linha do DOS, também conhecidas como. retornos de carro ou \!
user77376
11
O que há de errado comsed -e :a -e '/\\$/N; s/\\\n//; ta'
Isaac
18

É possivelmente mais fácil com o perl (já que o perl é como sed e awk, espero que seja aceitável para você):

perl -p -e 's/\\\n//'
camh
fonte
curta e simples, eu como aquele +1 E ele não pediu sed ou awk explicitamente
rudolfson
2

Esta não é uma resposta como tal. É uma questão paralela sed.

Especificamente, eu precisava seddesmontar o comando de Gilles, peça por peça, para entendê-lo ... Comecei a escrever algumas anotações e achei que poderia ser útil aqui para alguém ...

então aqui está ... o script sed de Gilles em formato documentado :


#!/bin/bash
#######################################
sed_dat="$HOME/ztest.dat"
while IFS= read -r line ;do echo "$line" ;done <<'END_DAT' >"$sed_dat"
foo bar \
bash \
baz
dude \
happy
yabba dabba 
doo
END_DAT

#######################################
sedexec="$HOME/ztest.sed"
while IFS= read -r line ;do echo "$line" ;done <<'END-SED' >"$sedexec"; \
sed  -nf "$sedexec" "$sed_dat"

  s/\\$//        # If a line has trailing '\', remove the '\'
                 #    
  t'Hold-append' # branch: Branch conditionally to the label 'Hold-append'
                 #         The condition is that a replacement was made.
                 #         The current pattern-space had a trailing '\' which  
                 #         was replaced, so branch to 'Hold-apend' and append 
                 #         the now-truncated line to the hold-space
                 #
                 # This branching occurs for each (successive) such line. 
                 #
                 # PS. The 't' command may be so named because it means 'on true' 
                 #     (I'm not sure about this, but the shoe fits)  
                 #
                 # Note: Appending to the hold-space introduces a leading '\n'   
                 #       delimiter for each appended line
                 #  
                 #   eg. compare the hex dump of the follow 4 example commands:  
                 #       'x' swaps the hold and patten spaces
                 #
                 #       echo -n "a" |sed -ne         'p' |xxd -p  ## 61 
                 #       echo -n "a" |sed -ne     'H;x;p' |xxd -p  ## 0a61
                 #       echo -n "a" |sed -ne   'H;H;x;p' |xxd -p  ## 0a610a61
                 #       echo -n "a" |sed -ne 'H;H;H;x;p' |xxd -p  ## 0a610a610a61

   # No replacement was made above, so the current pattern-space
   #   (input line) has a "normal" ending.

   x             # Swap the pattern-space (the just-read "normal" line)
                 #   with the hold-space. The hold-space holds the accumulation
                 #   of appended  "stripped-of-backslah" lines

   G             # The pattern-space now holds zero to many "stripped-of-backslah" lines
                 #   each of which has a preceding '\n'
                 # The 'G' command Gets the Hold-space and appends it to 
                 #   the pattern-space. This append action introduces another
                 #   '\n' delimiter to the pattern space. 

   s/\n//g       # Remove all '\n' newlines from the pattern-space

   p             # Print the pattern-space

   s/.*//        # Now we need to remove all data from the pattern-space
                 # This is done as a means to remove data from the hold-space 
                 #  (there is no way to directly remove data from the hold-space)

   x             # Swap the no-data pattern space with the hold-space
                 # This leaves the hold-space re-initialized to empty...
                 # The current pattern-space will be overwritten by the next line-read

   b             # Everything is ready for the next line-read. It is time to make 
                 # an unconditional branch  the to end of process for this line
                 #  ie. skip any remaining logic, read the next line and start the process again.

  :'Hold-append' # The ':' (colon) indicates a label.. 
                 # A label is the target of the 2 branch commands, 'b' and 't'
                 # A label can be a single letter (it is often 'a')
                 # Note;  'b' can be used without a label as seen in the previous command 

    H            # Append the pattern to the hold buffer
                 # The pattern is prefixed with a '\n' before it is appended

END-SED
#######
Peter.O
fonte
11
A solução de Neurino é bem simples, na verdade. Por falar em sed um pouco complicado, isso pode lhe interessar .
Gilles 'SO- stop be evil'
2

Ainda outra ferramenta de linha de comando comum seria ed, que por padrão modifica os arquivos no local e, portanto, deixa as permissões de arquivo inalteradas (para obter mais informações sobre edconsulte Editando arquivos com o editor de texto a partir de scripts )

str='
foo bar \
bash 1 \
bash 2 \
bash 3 \
bash 4 \
baz
dude \
happy
xxx
vvv 1 \
vvv 2 \
CCC
'

# We are using (1,$)g/re/command-list and (.,.+1)j to join lines ending with a '\'
# ?? repeats the last regex search.
# replace ',p' with 'wq' to edit files in-place
# (using Bash and FreeBSD ed on Mac OS X)
cat <<-'EOF' | ed -s <(printf '%s' "$str")
H
,g/\\$/s///\
.,.+1j\
??s///\
.,.+1j
,p
EOF
verdo
fonte
2

Usando o fato de que readno shell interpretará barras invertidas quando usadas sem -r:

$ while IFS= read line; do printf '%s\n' "$line"; done <file
foo bar bash baz
dude happy

Observe que isso também interpretará qualquer outra barra invertida nos dados.

Kusalananda
fonte
Não. Não removerá toda a barra invertida. Tente coma\\b\\\\\\\\\\\c
Isaac
@ Isaac Ah, talvez eu devesse ter dito "interpretar qualquer outra barra invertida"?
Kusalananda
1

Uma solução simples (r) que carrega o arquivo inteiro na memória:

sed -z 's/\\\n//g' file                   # GNU sed 4.2.2+.

Ou uma ainda curta que funciona para entender as linhas (de saída) (sintaxe GNU):

sed ':x;/\\$/{N;bx};s/\\\n//g' file

Em uma linha (sintaxe POSIX):

sed -e :x -e '/\\$/{N;bx' -e '}' -e 's/\\\n//g' file

Ou use o awk (se o arquivo for muito grande para caber na memória):

awk '{a=sub(/\\$/,"");printf("%s%s",$0,a?"":RS)}' file
Isaac
fonte
0

A versão para Mac baseada na solução @Giles ficaria assim

sed ':x
/\\$/{N; s|\\'$'\\n||; tx
}' textfile

Onde a principal diferença é como as novas linhas são representadas, e a combinação adicional de uma linha quebra-a

Andy
fonte
-1

Você pode usar o cpp, mas ele produz algumas linhas vazias onde mesclou a saída e algumas introduções que removo com o sed - talvez isso também possa ser feito com as opções e opções do cpp:

echo 'foo bar \
bash \
baz
dude \
happy' | cpp | sed 's/# 1 .*//;/^$/d'
foo bar bash baz
dude happy
Usuário desconhecido
fonte
Tem certeza de que cpp é uma solução? No seu exemplo, a echostring with entre aspas duplas já gera texto corrigido, portanto, cppé inútil. (Isso também se aplica ao seu sedcódigo.) Se você colocar a string entre aspas cppsimples , apenas remove as barras invertidas, mas não concatena as linhas. (A concatenação com cppfuncionaria se não haveria espaço antes as barras invertidas, mas então as palavras separadas seriam unidas sem separadores.)
manatwork
@manatwork: Outsch! :) Fiquei surpreso que o comando sed funcionou, mas é claro que não era o comando sed, mas o bash em si interpreta a quebra de linha de barra invertida como continuação da linha anterior.
usuário desconhecido
Usar cppassim ainda não concatena as linhas para mim. E o uso de sedé definitivamente desnecessário. Use cpp -P: “ -Pgeração de Inibição de linemarkers na saída do pré-processador.” - homem CPP
manatwork
Seu comando não funciona para mim: cpp: “-P: No such file or directory cpp: warning: '-x c' after last input file has no effect cpp: unrecognized option '-P:' cpp: no input filesA cpp --versionrevela cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- o que? O Ubuntu está corrigindo o cpp? Por quê? Eu esperava ler GNU ...
user unknown
Interessante. O Ubuntu de cppfato concatena as linhas e deixa alguns espaços em branco. Ainda mais interessante, a mesma versão 4.4.3-4ubuntu5.1 aqui aceita -P. No entanto, apenas elimina os marcadores de linha, as linhas vazias permanecem.
Manatwork