J'ai besoin d'append une nouvelle ligne avant toute ligne contenant un motif où nous pouvons supposer que le motif est toujours la première string de la ligne courante. Par exemple
This is a pattern This is a pattern
Je peux append une nouvelle ligne avec la command sed
sed -i 's/pattern\+/\n&/g' file
pour get la sortie
This is a pattern This is a pattern
Pour éviter que plusieurs nouvelles lignes soient ajoutées (en cas d'exécution multiple), je veux vérifier si la ligne avant que le model soit vide. Je sais que je peux le faire avec
if [ "$line" == "" ]; then
Mais comment puis-je déterminer la ligne précédente, d'un model correspondant, en premier lieu?
EDIT: Le motif peut se produire plusieurs fois.
Vous pouvez stocker la ligne précédente dans l'espace de cale:
sed ' /^pattern/{ x /./ { x s/^/\ / x } x } h'
Ce serait plus lisible avec awk
cependant:
awk '!previous_empty && /pattern/ {print ""} {previous_empty = $0 == ""; print}'
Comme les implémentations GNU de sed
ont une option -i
pour l'édition sur place, l'implémentation GNU de awk
as -i inplace
pour cela.
Mais comment puis-je déterminer la ligne précédente, d'un model correspondant, en premier lieu?
Hum … peut-être que ça va marcher.
En utilisant grep
et le commutateur -B
:
-B num, --before-context=num Print num lines of leading context before each match. See also the -A and -C options.
Considérez ceci infile:
cat infile foo bar baz
Maintenant, si je grep
pour la bar
, la ligne précédente devrait être vide:
if [[ $(grep -B 1 'bar' infile | head -1) == "" ]]; then echo "line is empty"; fi line is empty
Contrairement à l'utilisation de grep
sur baz
, où la ligne précédente n'est pas vide:
if [[ $(grep -B 1 'baz' infile | head -1) == "" ]]; then echo "line is empty"; fi <no output>
Une autre façon avec sed
:
sed -e 'tD' -e '$!N;/.\npattern/s/\n/&&/;:D' -e 'P;D' infile
Ceci a été expliqué en détail ici : c'est essentiellement un cycle N;P;D
où l'on count les nouvelles lignes que nous éditons, donc chaque fois que le script insère un \n
ewline il n'exécute que P
et D
sans N
afin de toujours avoir seulement deux lignes dans l'espace du motif.
Je sais que votre question portait initialement sur sed, mais il y a une réponse magnifiquement simple dans vim:
:g/.\npattern/norm o
Ou, si vous préférez exécuter cela entièrement à partir de la command line:
vim file -c "g/.\npattern/norm o" -c "wq"
La façon dont il fonctionne, c'est qu'il cherche une ligne qui correspond à l'expression rationnelle suivante:
.\npattern
qui est n'importe quelle ligne non vide suivie par votre model. Ensuite, pour chaque correspondance, elle applique la command suivante norm o
, qui ouvre une nouvelle ligne sous l'location actuel du slider.
Si nous avons gnused (la valeur par défaut dans Linux et beaucoup d'autres, et disponible pour tous)
sed -zri 's/([^\n]\n)(pattern)/\1\n\2/g' file
où
([^\n]\n)(pattern)
le motif après une ligne non vide -z
sépare les "lignes" par le caractère null (slurp le file) -r
avoir des expressions régulières étendues ex
, POSIX uniquement printf '%s\n' 0a '' . 1d 'g/pattern/-put | -,.!uniq' x | ex file
Résumé rapide des commands passées à ex
par printf
:
0a - append after line 0 - an empty line . - stop appending 1d - delete line 1 (the new empty line) into the unnamed register (aka buffer) g/pattern/-put | -,.!uniq g/pattern/ - for every line in the file matching "pattern" - - on the *previous* line, put - "put" (linewise append) the contents of the unnamed register | - and also do the following (still part of the g// command) -,. - take the previous and current lines !uniq - and run them through the external command "uniq" (replacing the lines with the output) x - save changes and exit
ex
vaut la peine d'apprendre. 🙂
Supposons que nous recadrions les exigences comme ceci:
Chaque fois que "pattern" apparaît à l'intérieur d'un paragraphe, nous aimerions que cette ligne commence un nouveau paragraphe, sauf lorsque "pattern" apparaît dans la première ligne d'un paragraphe.
De plus, cela ne nous dérange pas de normaliser le file pour qu'il y ait exactement une ligne vide entre les paragraphes, et pas de lignes vides égarées au début ou à la fin. *
Si ces conditions sont acceptables, nous pouvons profiter du mode paragraphe Awk (activé une valeur vide dans RS
):
awk 'BEGIN { RS=""; FS="\n" } { print sep $1; for (i = 2; i <= NF; i++) { if ($i ~ /pat/) print ""; print $i } sep=FS }'
En mode paragraphe, les loggings sont des paragraphes. Parce que nous utilisons \n
comme FS
, les champs $1, $2, ... $NF
correspondent à des lignes de paragraphes. Par exemple, si NF
est 5, nous avons affaire à un paragraphe de cinq lignes. Les nouvelles lignes qui séparent les paragraphes sont supprimées et chaque logging $0
ne contient que les nouvelles lignes intérieures entre les lignes, et la division des champs est effectuée sur celles-ci.
Un paragraphe a au less une ligne, parce que les paragraphes ne peuvent pas être vides: un paragraphe vide ressemble à deux nouvelles lignes consécutives qui font partie de la même séquence de séparation de paragraphe.
Donc, sans vérifier que NF
est au less 1, nous imprimons simplement la première ligne de paragraphe avec print sep $1
. La première fois autour, sep
est vide donc est sans conséquence; mais après le premier paragraphe, nous définissons sep
à une nouvelle ligne, de sorte que l' print sep $1
suivante print sep $1
générera la séparation des paragraphes.
Après avoir imprimé la première ligne, nous parcourons les lignes restantes, le cas échéant, et les imprimons. Ici, nous vérifions si chaque ligne correspond au model. Si c'est le cas, nous émettons une ligne vide supplémentaire avant de l'imprimer.