Comment combiner toutes les lignes qui se terminent par un caractère barre oblique inverse?


36

En utilisant un outil de ligne de commande commun comme sed ou awk, est-il possible de joindre toutes les lignes se terminant par un caractère donné, comme une barre oblique inverse?

Par exemple, étant donné le fichier:

foo bar \
bash \
baz
dude \
happy

Je voudrais obtenir cette sortie:

foo bar bash baz
dude happy

1
Passez le fichier cpp:)
imz - Ivan Zakharyaschev

Tant de réponses merveilleuses, j'aimerais pouvoir les marquer toutes comme la réponse! Merci pour le superbe regard sur awk, sed et perl, c'étaient d'excellents exemples.
Cory Klein

Réponses:


27

une solution sed plus courte et plus simple:

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

ou one-liner si vous utilisez GNU sed:

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

1
bon un… j'ai d'abord regardé ça et je ne pouvais pas le comprendre (donc ça ne faisait pas partie du panier trop dur) ... mais après un regard en profondeur sur la réponse de Gilles (qui a pris assez de temps) J'ai jeté un autre coup d'œil à votre réponse et cela a semblé remarquablement compréhensible. Je pense que je commence à comprendre sed:) ... vous ajoutez chaque ligne directement à l'espace-motif, et lorsqu'une ligne "normalement terminée" apparaît, la tout l’espace du modèle tombe à travers et les impressions automatiques (car il n’ya pas d’option -n) ... soignée! .. +1
Peter.O

@fred: merci, je pense que je commence aussi à comprendre sed, il offre de bons outils pour l'édition multiligne, mais comment les mélanger pour obtenir ce dont vous avez besoin n'est pas simple, ni la lisibilité est au top ...
neurino

Méfiez-vous des fins de ligne DOS, aka. chariot retourne ou \ r!
user77376

1
Quel est le problème avecsed -e :a -e '/\\$/N; s/\\\n//; ta'
Isaac

18

C'est peut-être plus facile avec perl (puisque perl est comme sed et awk, j'espère que c'est acceptable pour vous):

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

Bref et simple, j'aime bien celui-là +1 Et il n'a pas demandé explicitement sed ou awk
rudolfson


2

Ce n'est pas une réponse en tant que telle. C'est une question secondaire à propos de sed.

Plus précisément, je devais sedséparer les commandes de Gilles, pièce par pièce, pour le comprendre. J'ai commencé à écrire des notes dessus, puis j'ai pensé que cela pourrait être utile ici à quelqu'un ...

alors voila ... le script sed de Gilles au format documenté :


#!/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 solution de Neurino est assez simple en fait. En parlant de sed légèrement compliqué, cela peut vous intéresser .
Gilles, arrête de faire le mal

2

Un autre outil courant en ligne de commande serait celui edqui modifie par défaut les fichiers sur place et laisse donc les autorisations de fichier non modifiées (pour plus d'informations, edvoir Modification de fichiers avec l'éditeur de texte ed à 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

2

En utilisant le fait que readdans le shell interprétera les barres obliques inverses lorsqu’il est utilisé sans -r:

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

Notez que cela interprétera également toute autre barre oblique inverse dans les données.


Nan. Cela ne supprimera pas toutes les barres obliques inverses. Essayez aveca\\b\\\\\\\\\\\c
Isaac

@ Isaac Ah, j'aurais peut-être dû dire "interprète toute autre barre oblique inverse"?
Kusalananda

1

Une solution simple qui charge tout le fichier en mémoire:

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

Ou encore une version courte qui fonctionne et qui comprend les lignes (en sortie) (syntaxe GNU):

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

Sur une ligne (syntaxe POSIX):

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

Ou utilisez awk (si le fichier est trop gros pour tenir dans la mémoire):

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

0

La version Mac basée sur la solution @Giles ressemblerait à ceci

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

Où la différence principale réside dans la façon dont les nouvelles lignes sont représentées;


-1

Vous pouvez utiliser cpp, mais cela produit des lignes vides où il a fusionné la sortie, et une introduction que je supprime avec sed - peut-être que cela peut être fait avec cpp-flags et options:

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

Êtes-vous sûr cpp est une solution? Dans votre exemple, la echochaîne with entre guillemets génère déjà du texte redressé, elle cppest donc inutile. (Ceci s'applique également à votre sedcode.) Si vous mettez la chaîne entre guillemets cppsimples, supprime simplement les barres obliques inverses, mais ne concatène pas les lignes. (La concaténation avec cppfonctionnerait s'il n'y avait pas d'espace avant les barres obliques inverses, mais les mots séparés seraient joints sans séparateurs.)
manatwork

@manatwork: Outsch! :) J'étais étonné de constater que la commande sed fonctionnait, mais bien sûr, ce n'était pas la commande sed, mais la bash elle-même interprétait la barre oblique inversée comme une continuation de la ligne précédente.
utilisateur inconnu

Utiliser cppcomme ça ne me permet toujours pas de concaténer les lignes. Et l'utilisation de sedest définitivement inutile. Utilisation cpp -P: « -PInhibe la génération de marqueurs de ligne dans la sortie du préprocesseur.» - man cpp
manatwork

Votre commande ne fonctionne pas pour moi: 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 --versionrévèle cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- quoi? Ubuntu corrige cpp? Pourquoi? Je me serais attendu à lire GNU ...
utilisateur inconnu

Intéressant. Ubuntu cppconcatène effectivement les lignes et laisse quelques blancs. Encore plus intéressant, la même version 4.4.3-4ubuntu5.1 accepte ici -P. Cependant, seuls les marqueurs de lignes sont éliminés, les lignes vides restent.
Manatwork
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.