La lecture d'un fichier entier dans l'espace modèle est utile pour remplacer les retours à la ligne, etc. et il existe de nombreux cas qui conseillent ce qui suit:
sed ':a;N;$!ba; [commands...]'
Cependant, il échoue si l'entrée ne contient qu'une seule ligne.
Par exemple, avec une entrée sur deux lignes, chaque ligne est soumise à la commande de substitution:
$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt
Mais, avec une entrée sur une seule ligne, aucune substitution n'est effectuée:
$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc
Comment écrire une sed
commande pour lire toutes les entrées en même temps et ne pas avoir ce problème?
sed -z
option de GNU . Si votre fichier n'est pas nul, il sera lu jusqu'à la fin du fichier! Trouvé à partir de ceci: stackoverflow.com/a/30049447/582917Réponses:
Il y a toutes sortes de raisons pour lesquelles la lecture d'un fichier entier dans l'espace modèle peut mal tourner. Le problème logique de la question entourant la dernière ligne est un problème courant. Il est lié au
sed
cycle de ligne de - lorsqu'il n'y a plus de lignes et qu'ilsed
rencontre EOF - il arrête le traitement. Et donc si vous êtes sur la dernière ligne et que vous demandezsed
à en obtenir un autre, cela va s'arrêter là et ne rien faire de plus.Cela dit, si vous avez vraiment besoin de lire un fichier entier dans l'espace modèle, il vaut probablement la peine d'envisager un autre outil de toute façon. Le fait est,
sed
est éponyme de l' éditeur de flux - il est conçu pour fonctionner une ligne - ou un bloc de données logique - à la fois.Il existe de nombreux outils similaires qui sont mieux équipés pour gérer des blocs de fichiers complets.
ed
etex
, par exemple, peut faire beaucoup de ce quised
peut faire et avec une syntaxe similaire - et bien plus encore - mais plutôt que de fonctionner uniquement sur un flux d'entrée tout en le transformant en sortie comme il lesed
fait, ils conservent également des fichiers de sauvegarde temporaires dans le système de fichiers . Leur travail est mis en mémoire tampon sur le disque selon les besoins, et ils ne s'arrêtent pas brusquement à la fin du fichier (et ont tendance à imploser beaucoup moins souvent sous la pression du tampon) . De plus, ils offrent de nombreuses fonctions utiles quised
- comme celles qui n'ont tout simplement pas de sens dans un contexte de flux - comme les marques de ligne, l'annulation, les tampons nommés, la jointure, etc.sed
La principale force de la société est sa capacité à traiter les données dès qu'elles les lisent - rapidement, efficacement et en continu. Lorsque vous récupérez un fichier, vous le jetez et vous avez tendance à rencontrer des problèmes de casse comme le dernier problème de ligne que vous mentionnez, des dépassements de mémoire tampon et des performances épouvantables - à mesure que les données qu'il analyse augmentent en longueur, le temps de traitement d'un moteur d'expression régulière lors de l'énumération des correspondances augmente de façon exponentielle .En ce qui concerne ce dernier point, soit dit en passant: même si je comprends que l'exemple
s/a/A/g
est très probablement un exemple naïf et n'est probablement pas le script réel que vous souhaitez rassembler dans une entrée, vous pourriez trouver utile de vous familiariser avecy///
. Si vous vous retrouvez souvent eng
train de substituer un seul caractère par un autre à un lob, celay
pourrait vous être très utile. C'est une transformation par opposition à une substitution et est beaucoup plus rapide car elle n'implique pas une expression rationnelle. Ce dernier point peut également être utile lors de la tentative de conservation et de répétition d'//
adresses vides car il ne les affecte pas mais peut être affecté par celles-ci. Dans tous les cas,y/a/A/
c'est un moyen plus simple d'accomplir la même chose - et les échanges sont également possibles comme:y/aA/Aa/
qui échangeraient tous les majuscules / minuscules comme sur une ligne les uns pour les autres.Vous devez également noter que le comportement que vous décrivez n'est vraiment pas censé se produire de toute façon.
De GNU
info sed
dans la section BOGUES RAPPORTS COMMUNS :N
commande sur la dernière ligneLa plupart des versions de
sed
exit n'impriment rien lorsque laN
commande est émise sur la dernière ligne d'un fichier. GNUsed
imprime l'espace de motif avant de quitter, à moins bien sûr que le-n
commutateur de commande n'ait été spécifié. Ce choix se fait par conception.Par exemple, le comportement de
sed N foo bar
dépendrait de si foo a un nombre pair ou impair de lignes. Ou, lors de l'écriture d'un script pour lire les quelques lignes suivantes après une correspondance de modèle, les implémentations traditionnelles desed
vous forceraient à écrire quelque chose comme/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
au lieu de juste/foo/{ N;N;N;N;N;N;N;N;N; }
.Dans tous les cas, la solution de contournement la plus simple consiste à utiliser des
$d;N
scripts reposant sur le comportement traditionnel ou à définir laPOSIXLY_CORRECT
variable sur une valeur non vide.La
POSIXLY_CORRECT
variable d'environnement est mentionnée car POSIX spécifie que sised
rencontre EOF lors d'une tentative,N
elle doit se fermer sans sortie, mais la version GNU rompt intentionnellement avec la norme dans ce cas. Notez également que même si le comportement est justifié ci-dessus, l'hypothèse est que le cas d'erreur est celui de l'édition de flux - et non la fusion d'un fichier entier en mémoire.La norme définit
N
ainsi le comportement de:N
Ajoutez la ligne d'entrée suivante, moins sa ligne terminale
\n
, à l'espace de motif, en utilisant une ligne intégrée\n
pour séparer le matériau ajouté du matériau d'origine. Notez que le numéro de ligne actuel change.Si aucune ligne d'entrée suivante n'est disponible, le
N
verbe de commande doit se ramifier à la fin du script et quitter sans démarrer un nouveau cycle ou copier l'espace de modèle sur la sortie standard.Sur cette note, il y a d'autres GNU-ismes démontrés dans la question - en particulier l'utilisation des crochets d'
:
étiquette, deb
ranch et{
de contexte de fonction}
. En règle générale, toutesed
commande qui accepte un paramètre arbitraire est\n
censée être délimitée au niveau d'une ligne électronique dans le script. Donc les commandes ...... sont tous très susceptibles de fonctionner de manière irrégulière en fonction de l'
sed
implémentation qui les lit. Portablement, ils devraient être écrits:La même chose vaut pour
r
,w
,t
,a
,i
etc
(et peut - être un peu plus que je suis oublier pour le moment) . Dans presque tous les cas, ils pourraient également être écrits:... où la nouvelle
-e
instruction xecution\n
remplace le délimiteur ewline. Donc, là où leinfo
texte GNU suggère qu'une implémentation traditionnellesed
vous obligerait à faire :... ça devrait plutôt être ...
... bien sûr, ce n'est pas vrai non plus. Écrire le script de cette façon est un peu idiot. Il existe des moyens beaucoup plus simples de faire de même, comme:
... qui imprime:
... car la
t
commande est - comme la plupart dessed
commandes - dépend du cycle de ligne pour rafraîchir son registre de retour et ici le cycle de ligne est autorisé à faire la plupart du travail. C'est un autre compromis que vous faites lorsque vous slurpez un fichier - le cycle de ligne ne se rafraîchit plus jamais, et de nombreux tests se comporteront anormalement.La commande ci-dessus ne risque pas de dépasser la saisie car elle ne fait que des tests simples pour vérifier ce qu'elle lit en le lisant. Avec
H
old, toutes les lignes sont ajoutées à l'espace d'attente, mais si une ligne correspond,/foo/
elle remplace l'h
ancien espace. Les tampons sont ensuitex
modifiés et unes///
substitution conditionnelle est tentée si le contenu du tampon correspond au//
dernier motif adressé. En d'autres termes,//s/\n/&/3p
tente de remplacer le troisième retour à la ligne dans l'espace d'attente par lui-même et d'imprimer les résultats si l' espace d'attente correspond actuellement/foo/
. Si celat
réussit, le script se branche sur l' étiquetten
otd
elete - qui fait un tourl
et termine le script.Dans le cas où les deux
/foo/
et une troisième nouvelle ligne ne peuvent pas être appariés ensemble dans l'espace de retenue, alors,//!g
ils écraseront le tampon s'ils/foo/
ne sont pas appariés, ou, s'ils sont appariés, ils écraseront le tampon si une\n
ewline n'est pas appariée (remplaçant ainsi/foo/
par lui-même) . Ce petit test subtil empêche le tampon de se remplir inutilement pendant de longues périodes de non/foo/
et garantit que le processus reste accrocheur car l'entrée ne s'accumule pas. En cas de non/foo/
ou d'//s/\n/&/3p
échec, les tampons sont à nouveau échangés et chaque ligne, sauf la dernière, est supprimée.Ce dernier - la dernière ligne
$!d
- est une simple démonstration de la façon dont unsed
script descendant peut être fait pour gérer facilement plusieurs cas. Lorsque votre méthode générale consiste à tailler les cas indésirables en commençant par les plus généraux et en travaillant vers les cas les plus spécifiques, les cas marginaux peuvent être plus facilement traités car ils sont simplement autorisés à passer à la fin du script avec vos autres données souhaitées et quand tout vous enveloppe avec les seules données que vous souhaitez. Cependant, il peut être beaucoup plus difficile de récupérer de tels cas de bord en boucle fermée.Et voici donc la dernière chose que j'ai à dire: si vous devez vraiment extraire un fichier entier, vous pouvez vous tenir à faire un peu moins de travail en vous appuyant sur le cycle de ligne pour le faire pour vous. En règle générale, vous utiliseriez
N
ext etn
ext pour l' anticipation - car ils avancent avant le cycle de ligne. Plutôt que d'implémenter de manière redondante une boucle fermée dans une boucle - comme lesed
cycle de ligne est de toute façon une simple boucle de lecture - si votre but est uniquement de collecter des entrées sans discernement, il est probablement plus facile de le faire:... qui rassemblera l'intégralité du fichier ou fera faillite.
une note latérale sur
N
et le comportement de dernière ligne ...la source
H
premier est beau.:a;$!{N;ba}
comme je le mentionne ci-dessus - il est plus facile d'utiliser le formulaire standard à long terme lorsque vous essayez d'exécuter des expressions rationnelles sur des systèmes inconnus. Mais ce n'était pas vraiment ce que je voulais dire: vous implémentez une boucle fermée - vous ne pouvez pas aussi facilement entrer au milieu de cela lorsque vous le souhaitez que vous le feriez plutôt en vous ramifiant - en élaguant les données indésirables - et en laissant le cycle se produire. C'est comme une chose descendante - tout cesed
qui se produit est le résultat direct de ce qu'il vient de faire. Peut-être que vous le voyez différemment - mais si vous l'essayez, vous trouverez peut-être que le script est plus facile.Il échoue car la
N
commande vient avant la correspondance de modèle$!
(pas la dernière ligne) et sed se ferme avant d'effectuer tout travail:Cela peut être facilement corrigé pour fonctionner avec une entrée sur une seule ligne (et en fait pour être plus clair dans tous les cas) en regroupant simplement les commandes
N
etb
après le modèle:Cela fonctionne comme suit:
:a
créer une étiquette nommée 'a'$!
sinon la dernière ligne, alorsN
ajoutez la ligne suivante à l'espace de motif (ou quittez s'il n'y a pas de ligne suivante) etba
branchez (allez à) l'étiquette 'a'Malheureusement, ce n'est pas portable (car il repose sur des extensions GNU), mais l'alternative suivante (suggérée par @mikeserv) est portable:
la source
:a;N;$!ba;
.