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'il sed
rencontre EOF - il arrête le traitement. Et donc si vous êtes sur la dernière ligne et que vous demandez sed
à 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
et ex
, par exemple, peut faire beaucoup de ce qui sed
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 le sed
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 qui sed
- 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 avec y///
. Si vous vous retrouvez souvent en g
train de substituer un seul caractère par un autre à un lob, cela y
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 :
La POSIXLY_CORRECT
variable d'environnement est mentionnée car POSIX spécifie que si sed
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, de b
ranch et {
de contexte de fonction }
. En règle générale, toute sed
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 ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... 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:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
La même chose vaut pour r
, w
, t
, a
, i
et c
(et peut - être un peu plus que je suis oublier pour le moment) . Dans presque tous les cas, ils pourraient également être écrits:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... où la nouvelle -e
instruction xecution \n
remplace le délimiteur ewline. Donc, là où le info
texte GNU suggère qu'une implémentation traditionnelle sed
vous obligerait à faire :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... ça devrait plutôt être ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... 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:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... qui imprime:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... car la t
commande est - comme la plupart des sed
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 ensuite x
modifiés et une s///
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 cela t
réussit, le script se branche sur l' étiquette n
ot d
elete - qui fait un tour l
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 un sed
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 et n
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 le sed
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:
sed 'H;1h;$!d;x;...'
... qui rassemblera l'intégralité du fichier ou fera faillite.
une note latérale sur N
et le comportement de dernière ligne ...
même si je n'ai pas les outils à ma disposition pour tester, considérez que N
lors de la lecture et de l' édition sur place se comporte différemment si le fichier édité est le fichier de script pour la prochaine lecture.