J'apprends sed. Tout semblait bien se passer jusqu'à ce que je tombe sur le N (multiligne ensuite). J'ai créé ce fichier (guide.txt) à des fins de pratique / compréhension / contexte. Voici le contenu dudit fichier ...
This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator
Mon objectif est donc de remplacer TOUTES les instances de "Network Administrator" par "System User". Parce que la première instance de "Administrateur réseau" est séparée par une nouvelle ligne (\ n), j'ai besoin de l'opérateur suivant multiligne (N) pour ajouter la ligne qui commence par "Administrateur" avec la ligne précédente se terminant par "Réseau \ n" . Aucun problème. Mais je veux également attraper toutes les autres instances monolignes "Administrateur réseau".
De mes recherches, j'ai appris que j'aurais besoin de deux commandes de substitution; un pour la chaîne séparée par la nouvelle ligne et un pour les autres. De plus, il y a du jive à cause de la dernière ligne contenant la correspondance de substitution et la multi-ligne suivante. Alors je crée ça ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt
Cela renvoie ces résultats ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User
Je pensais que la substitution sur une seule ligne intercepterait toutes les instances "normales" de "Administrateur réseau" et la remplacerait par "Utilisateur système", tandis que l'instruction multiligne fonctionnerait comme par magie sur l'instance séparée par une nouvelle ligne, mais comme vous peut le voir retourné, ce que je considère, des résultats inattendus.
Après quelques tripotages, j'ai atterri là-dessus ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt
Et voilà, j'obtiens la sortie souhaitée de ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Pourquoi cela fonctionne-t-il et le script sed original ne fonctionne pas? Je veux vraiment comprendre ça.
Merci d'avance pour votre aide.
Réponses:
Pendant que vous apprenez
sed
, je prendrai le temps d'ajouter à la réponse de @ John1024:1) Veuillez noter que vous utilisez
\n
dans la chaîne de remplacement. Cela fonctionne dans GNUsed
, mais ne fait pas partie de POSIX, il insérera donc une barre oblique inverse et unn
dans de nombreux autressed
(l'utilisation\n
dans le modèle est portable, btw).Au lieu de cela, je suggère de faire
s/Network\([[:space:]]\)Administrator/System\1User/g
: le[[:space:]]
correspondra à la nouvelle ligne ou à l'espace, vous n'avez donc pas besoin de deuxs
commandes, mais combinez-les en une. En l'entourant de\(...\)
vous pouvez vous y référer dans le remplacement: le\1
sera remplacé par tout ce qui a été apparié dans la première paire de\(\)
.2) Pour faire correspondre correctement les modèles sur deux lignes, vous devez connaître le
N;P;D
modèle:Le
N
est toujours ajouter la ligne suivante (sauf pour la dernière ligne, c'est pourquoi il est "adressé" avec$!
(= sinon la dernière ligne; vous devriez toujours envisager de précéderN
avec$!
pour éviter de terminer accidentellement le script). Ensuite, après le remplacement, lesP
impressions uniquement la première ligne de l'espace de motif et laD
supprime cette ligne et commence le cycle suivant avec les restes de l'espace de motif (sans lire la ligne suivante). C'est probablement ce que vous aviez initialement prévu.N'oubliez pas ce modèle, vous en aurez souvent besoin.
3) Un autre modèle utile pour l'édition multiligne, surtout lorsque plus de deux lignes sont impliquées: Maintenez la collecte d'espace, comme je l'ai suggéré à John:
Je le répète pour l'expliquer:
H
ajoute chaque ligne à l'espace d'attente. Comme cela entraînerait une nouvelle ligne supplémentaire avant la première ligne, la première ligne doit être déplacée au lieu d'être ajoutée avec1h
. Ce qui$!d
signifie "pour toutes les lignes sauf la dernière, supprimez l'espace de motif et recommencez". Ainsi, le reste du script n'est exécuté que pour la dernière ligne. À ce stade, le fichier entier est collecté dans l'espace d'attente (ne l'utilisez donc pas pour les très gros fichiers!) Et leg
déplace vers l'espace de motif, de sorte que vous pouvez effectuer tous les remplacements à la fois comme vous le pouvez avec l'-z
option de GNUsed
.C'est un autre schéma utile que je suggère de garder à l'esprit.
la source
Tout d'abord, notez que votre solution ne fonctionne pas vraiment. Considérez ce fichier de test:
Et puis exécutez la commande:
Le problème est que le code ne remplace pas le dernier
Network\nAdministrator
.Cette solution fonctionne:
Nous pouvons également l'appliquer à votre
guide.txt
:La clé est de continuer à lire en lignes jusqu'à ce que vous en trouviez une qui ne se termine pas par
Network
. Lorsque cela est accompli, les substitutions peuvent être effectuées.Note de compatibilité: Toutes les utilisations ci-dessus
\n
dans le texte de remplacement. Cela nécessite GNU sed. Cela ne fonctionnera pas sur BSD / OSX sed.[Pointe du chapeau à Philippos .]
Version multiligne
Si cela aide à clarifier, voici la même commande répartie sur plusieurs lignes:
Comment ça fonctionne
:a
Cela crée une étiquette
a
./Network$/{ $!{N;ba} }
Si cette ligne se termine par
Network
, alors, si ce n'est pas la dernière ligne ($!
), lisez et ajoutez la ligne suivante (N
) et ramenez-la à labela
(ba
).s/Network\nAdministrator/System\nUser/g
Faites la substitution avec la nouvelle ligne intermédiaire.
s/Network Administrator/System User/g
Effectuez la substitution avec le blanc intermédiaire.
Solution plus simple (GNU uniquement)
Avec GNU sed ( pas BSD / OSX), nous n'avons besoin que d'une seule commande de substitution:
Et sur le
guide.txt
dossier:Dans ce cas,
-z
indique à sed de lire jusqu'au premier caractère NUL. Étant donné que les fichiers texte n'ont jamais de caractère nul, cela a pour effet de lire l'intégralité du fichier à la fois. On peut alors faire la substitution sans se soucier de manquer une ligne.Cette méthode n'est pas bonne si le fichier est énorme (ce qui signifie généralement des gigaoctets). Si elle est si grande, alors la lecture en une seule fois peut épuiser la RAM du système.
Solution qui fonctionne à la fois sur GNU et BSD sed
Comme l'a suggéré Phillipos , voici une solution portable:
la source
Network Administrator
est réparti entre la première et la deuxième ligne de cette paire, votre solution réussit la substitution. Il imprime ensuite ces deux lignes et lit dans la paire suivante. Si, cependant, la deuxième ligne de la première paire se termine parNetwork
et la première ligne de la deuxième paire commence parAdministrator
, le code la manque. Mon code évite cela en lisant les lignes jusqu'à ce qu'il en trouve une qui ne se termine pasNetwork
.sed
: Le\n
remplacement n'est pas défini dans la norme.sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1User/g'
est un moyen portable de le faire.