Come puoi combinare tutte le linee che terminano con un carattere barra rovesciata?


36

Utilizzando uno strumento a riga di comando comune come sed o awk, è possibile unire tutte le righe che terminano con un determinato carattere, come una barra rovesciata?

Ad esempio, dato il file:

foo bar \
bash \
baz
dude \
happy

Vorrei ottenere questo risultato:

foo bar bash baz
dude happy

1
Passa il file cpp:)
imz - Ivan Zakharyaschev

Così tante risposte meravigliose, vorrei poterle contrassegnare tutte come risposta! Grazie per l'ottimo sguardo a awk, sed e perl, questi erano ottimi esempi.
Cory Klein,

Risposte:


27

una soluzione sed più breve e semplice:

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

o one-liner se si utilizza GNU sed:

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

1
buono ... L'ho guardato inizialmente e non sono riuscito a capirlo (quindi non è entrato nel cestino troppo duro) ... ma dopo uno sguardo approfondito alla risposta di Gilles (che è durata parecchio tempo) Ho dato un'altra occhiata alla tua risposta e mi è sembrato straordinariamente comprensibile, penso che sto iniziando a capire sed:) ... Stai aggiungendo ogni riga direttamente allo spazio del modello, e quando arriva una linea "normalmente terminata", il cade l'intero spazio del motivo e le stampe automatiche (perché non esiste un'opzione -n) ... pulito! .. +1
Peter

@fred: grazie penso che sto iniziando a capire anche sed, offre ottimi strumenti per l'editing multilinea ma come confonderli per ottenere ciò di cui hai bisogno non è semplice né la leggibilità è al top ...
neurino

Attenzione alle terminazioni di linea DOS, aka. ritorni a capo oppure \ r!
user77376,

1
Cosa c'è che non vased -e :a -e '/\\$/N; s/\\\n//; ta'
Isaac il

18

È forse più semplice con perl (poiché perl è come sed e awk, spero sia accettabile per te):

perl -p -e 's/\\\n//'

breve e semplice, mi piace quello +1 E non ha chiesto sed o awk esplicitamente
Rudolfson,


2

Questa non è una risposta in quanto tale. È un problema secondario sed.

In particolare, avevo bisogno di sedsmontare il comando di Gilles pezzo per pezzo per capirlo ... Ho iniziato a scrivere alcune note su di esso, e poi ho pensato che potesse essere utile qui a qualcuno ...

quindi eccolo qui ... lo script sed di Gilles in formato documentato :


#!/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
#######

1
La soluzione di Neurino è piuttosto semplice in realtà. Parlando di sed leggermente complicati, questo potrebbe interessarti .
Gilles 'SO- smetti di essere malvagio'

2

Ancora un altro strumento da riga di comando comune sarebbe ed, che per impostazione predefinita modifica i file sul posto e quindi lascia le autorizzazioni dei file non modificate (per ulteriori informazioni su edVedere Modifica dei file con l'editor di testo ed dagli script )

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

2

Usando il fatto che readnella shell interpreterà le barre rovesciate se usato senza -r:

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

Si noti che ciò interpreterà anche qualsiasi altra barra rovesciata nei dati.


No. Non rimuoverà tutta la barra rovesciata. Prova cona\\b\\\\\\\\\\\c
Isaac il

@Isaac Ah, forse avrei dovuto dire "interpretare qualsiasi altra barra inversa"?
Kusalananda

1

Una semplice soluzione (r) che carica l'intero file in memoria:

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

O uno ancora breve che funziona comprendendo le linee (output) (sintassi GNU):

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

Su una riga (sintassi POSIX):

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

Oppure usa awk (se il file è troppo grande per adattarsi alla memoria):

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

0

La versione per Mac basata sulla soluzione @Giles sarebbe simile a questa

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

Dove la differenza principale è come sono rappresentate le nuove linee e la combinazione di ulteriori righe in una riga la interrompe


-1

Puoi usare cpp, ma produce alcune righe vuote in cui ha unito l'output e qualche introduzione che rimuovo con sed - forse può essere fatto anche con cpp-flags e opzioni:

echo 'foo bar \
bash \
baz
dude \
happy' | cpp | sed 's/# 1 .*//;/^$/d'
foo bar bash baz
dude happy

Sei sicuro che cpp sia una soluzione? Nel tuo esempio la echostringa with tra virgolette genera già testo raddrizzato, quindi cppè inutile. (Questo vale anche per il tuo sedcodice.) Se inserisci la stringa tra virgolette singole, cpprimuove solo le barre rovesciate ma non concatena le linee. (La concatenazione con cppavrebbe funzionato se non ci fosse spazio prima delle barre rovesciate, ma poi le parole separate sarebbero state unite senza separatori.)
manatwork

@manatwork: Outsch! :) Sono rimasto sbalordito dal fatto che il comando sed abbia funzionato, ma ovviamente non era il comando sed, ma lo stesso bash interpreta il backslash-linebreak come continuazione della riga precedente.
utente sconosciuto

Usare cppcosì non concatena ancora le linee per me. E l'uso di non sedè assolutamente necessario. Usa cpp -P: " -PInibisci la generazione di marcatori di linea nell'output del preprocessore." - man cpp
manatwork

Il tuo comando non funziona per me: 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 --versionrivela cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- cosa? Ubuntu sta patching cpp? Perché? Mi sarei aspettato di leggere GNU ...
utente sconosciuto

Interessante. Ubuntu dicpp infatti concatena le linee e lascia degli spazi vuoti. Ancora più interessante, la stessa versione 4.4.3-4ubuntu5.1 qui accetta -P. Tuttavia elimina solo i marcatori di linea, le linee vuote rimangono.
arte
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.