Wie können Sie alle Zeilen, die mit einem Backslash enden, kombinieren?

36

Kann man mit einem gängigen Befehlszeilenprogramm wie sed oder awk alle Zeilen verbinden, die mit einem bestimmten Zeichen enden, wie z. B. einem Backslash?

Zum Beispiel angesichts der Datei:

foo bar \
bash \
baz
dude \
happy

Ich möchte diese Ausgabe erhalten:

foo bar bash baz
dude happy
Cory Klein
quelle
1
Pass die Datei durch cpp:)
imz - Ivan Zakharyaschev
So viele wundervolle Antworten, ich wünschte, ich könnte sie alle als Antwort markieren! Vielen Dank für den tollen Blick auf awk, sed und perl, das waren großartige Beispiele.
Cory Klein
Beachten Sie, dass es in der sedFAQ
Stéphane Chazelas

Antworten:

27

Eine kürzere und einfachere Lösung:

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

oder einzeilig bei Verwendung von GNU sed:

sed ':x; /\\$/ { N; s/\\\n//; tx }' textfile
Neurino
quelle
1
Guter ... Ich habe mir das ursprünglich angeschaut und konnte es nicht verstehen (also nicht in den zu harten Korb) ... aber nach einem gründlichen Blick auf Gilles 'Antwort (was ziemlich lange gedauert hat) Ich habe mir Ihre Antwort noch einmal angesehen und sie sah bemerkenswert verständlich aus. Ich glaube, ich fange an zu verstehen sed:) ... Sie hängen jede Zeile direkt an den Musterraum an, und wenn eine "normal endende" Zeile vorkommt, folgt die Der gesamte Musterbereich fällt durch und wird automatisch gedruckt (da es keine Option -n gibt) ... ordentlich! .. +1
Peter.O
@fred: danke, ich glaube, ich fange an, sed auch zu verstehen, es bietet nette Werkzeuge für die mehrzeilige Bearbeitung, aber wie man sie mischt, um das zu bekommen, was man braucht, ist nicht einfach, noch ist die Lesbarkeit oben ...
neurino
Vorsicht vor DOS-Zeilenenden, auch bekannt als. Wagenrücklauf oder \ r!
User77376
1
Was ist los mitsed -e :a -e '/\\$/N; s/\\\n//; ta'
Isaac
18

Mit Perl ist es möglicherweise am einfachsten (da Perl wie sed und awk ist, hoffe ich, dass es für Sie akzeptabel ist):

perl -p -e 's/\\\n//'
camh
quelle
kurz und einfach, das gefällt mir +1 Und er hat nicht explizit nach sed oder awk
gefragt
2

Dies ist keine Antwort als solche. Es handelt sich um ein Nebenthema sed.

Insbesondere musste ich das Gilles- sedKommando Stück für Stück auseinander nehmen, um es zu verstehen ... Ich fing an, einige Notizen darüber zu schreiben, und dachte dann, dass es hier für jemanden nützlich sein könnte ...

also hier ist es ... Gilles 'sed- Skript in dokumentiertem Format:


#!/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
quelle
1
Neurinos Lösung ist eigentlich ziemlich einfach. Apropos leicht kompliziertes Sed, das könnte Sie interessieren .
Gilles 'SO- hör auf böse zu sein'
2

Ein weiteres gebräuchliches Befehlszeilentool ist das ed, das Dateien standardmäßig direkt ändert und daher die Dateiberechtigungen unverändert lässt (weitere Informationen finden edSie unter Bearbeiten von Dateien mit dem Texteditor ed aus Skripten ).

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
quelle
2

Verwenden der Tatsache, dass readin der Shell umgekehrte Schrägstriche interpretiert werden, wenn sie ohne Folgendes verwendet werden -r:

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

Beachten Sie, dass dies auch alle anderen umgekehrten Schrägstriche in den Daten interpretiert .

Kusalananda
quelle
Nee. Es werden nicht alle Backslashs entfernt. Versuchen Sie es mita\\b\\\\\\\\\\\c
Isaac
@Isaac Ah, vielleicht hätte ich sagen sollen "interpretiere irgendeinen anderen Backslash"?
Kusalananda
1

Eine einfache (r) Lösung, die die gesamte Datei in den Speicher lädt:

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

Oder eine noch kurze, die das Verstehen von (Ausgabe-) Zeilen (GNU-Syntax) unterstützt:

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

In einer Zeile (POSIX-Syntax):

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

Oder benutze awk (wenn die Datei zu groß ist, um in den Speicher zu passen):

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

Die auf der @ Giles-Lösung basierende Mac-Version würde so aussehen

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

Wenn der Hauptunterschied darin besteht, wie Zeilenumbrüche dargestellt werden, und wenn weitere Zeilen zu einer Zeile zusammengefasst werden, wird der Unterschied aufgehoben

Andy
quelle
-1

Sie können cpp verwenden, aber es entstehen einige leere Zeilen, in denen die Ausgabe zusammengeführt wird, und eine Einführung, die ich mit sed entferne. Vielleicht kann dies auch mit cpp-Flags und Optionen geschehen:

echo 'foo bar \
bash \
baz
dude \
happy' | cpp | sed 's/# 1 .*//;/^$/d'
foo bar bash baz
dude happy
Benutzer unbekannt
quelle
Sind Sie sicher, dass cpp es eine Lösung gibt? In Ihrem Beispiel gibt die echoZeichenfolge mit Anführungszeichen bereits gerichteten Text aus, cppist also sinnlos. (Dies gilt auch für Ihren sedCode.) Wenn Sie die Zeichenfolge in einfache Anführungszeichen setzen,cpp nur die umgekehrten Schrägstriche entfernt, die Zeilen jedoch nicht verkettet. (Die Verkettung mit cppwürde funktionieren, wenn vor den Backslashes kein Leerzeichen wäre, aber dann würden die einzelnen Wörter ohne Trennzeichen verbunden.)
manatwork
@manatwork: Outsch! :) Ich war erstaunt, dass der sed-Befehl funktioniert hat, aber natürlich war es nicht der sed-Befehl, aber die Bash selbst interpretiert Backslash-Zeilenumbruch als Fortsetzung der vorherigen Zeile.
Benutzer unbekannt
Mit cppso etwas verketten sich die Zeilen für mich noch nicht. Und die Verwendung von sedist definitiv unnötig. Verwendung cpp -P: " -PGenerierung von Linemarken in der Ausgabe des Präprozessors unterbinden." - man cpp
unterbinden
Dein Befehl funktioniert bei mir nicht: 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 --versionverrät cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- was? Ubuntu patcht cpp? Warum? Ich hätte erwartet, GNU zu lesen ...
Benutzer unbekannt
Interessant. Ubuntus cppverkettet tatsächlich die Zeilen und hinterlässt einige Leerzeichen. Noch interessanter ist, dass dieselbe Version 4.4.3-4ubuntu5.1 hier akzeptiert -P. Es werden jedoch nur die Linienmarkierungen entfernt, die leeren Linien bleiben erhalten.
Manatwork