Ed: l'éditeur standard
Introduction
Ed est un éditeur de texte datant de 1969, aujourd'hui encore, fait partie du standard POSIX normalement respecté par tout système UNIX ou Linux. A l'époque il n'était pas possible à cause des ressources limitées d'afficher les modifications effectuées sur le texte en temps réel comme n'importe quel éditeur récent le fait. On interragit avec le texte par commandes interposées.
Ed est à l'origine de nombreux outils comme sed, ex, vi, ou vim qui reprennent tout ou partie de ses commandes appréciées pour leur expressivité et leur concision. Étant donné que ces outils sont maintenant communs cette introduction suppose une certaine familiarité du lecteur avec au moins vi ou sed bien qu'il soit possible de la suivre sans avoir utilisé ces outils auparavant.
À la rencontre du monstre
Lançons la bête puis essayons de la quitter :
$ ed
^C
?
quit
?
q
Par défaut Ed ne possède aucun prompt permettant de distinguer l'attente d'une commande d'une attente de texte "brut". De même l'affichage des erreurs est succint : un point d'interrogation marque l'incompréhension. La commande 'h' permet d'afficher des informations sur la dernière erreur rencontrée:
$ ed
quit
?
h
Invalid command suffix
q
Comme on peut le voir Ed n'a pas compris la commande "quit", mais semble reconnaître le début. C'est tout à fait normal car 'q' est la commande permettant de quitter l'éditeur. Toutes les commandes vous l'aurez deviné ne sont constituées que d'une seule lettre.
Afin de faciliter l'édition nous allons définir un alias pour mettre en place un prompt et automatiquement afficher les messages d'erreur :
alias ed='ed --prompt=">>> " --verbose '
Tout les exemples du reste de cette introduction employeront cet alias.
Édition basique
Créer un nouveau fichier
Nous allons maintenant créer un nouveau fichier pour explorer les possibilités :
$ ed myfile
myfile: No such file or directory
>>> # La commande # permet de faire des commentaires dans ed
Entrons du texte avec 'a' pour 'append'.
>>> a
Note the consistent user interface and error reporting.
Ed is generous enough to flag errors,
yet prudent enough not to overwhelm the novice with verbosity.
.
Toute entrée de texte se termine par un point solitaire. Après cette edition nous sommes placés à la fin du texte entré comme on peut facilement le constater avec la commande 'p' pour 'print' ou encore 'n' pour 'number' qui affichent respectivement la ligne courante et la ligne avec son numéro.
>>> p
yet prudent enough not to overwhelm the novice with verbosity.
>>> n
3 yet prudent enough not to overwhelm the novice with verbosity.
On peut afficher une ligne en particulier en plaçant son numéro devant la commande, et même employer une plage de nombre pour un affichage multiple :
>>> 2p
Ed is generous enough to flag errors,
>>> 1,3n
1 Note the consistent user interface and error reporting.
2 Ed is generous enough to flag errors,
3 yet prudent enough not to overwhelm the novice with verbosity.
Il est possible d'utiliser ces adresses avec n'importe quelle commande Ed, par exemple 'i' pour 'insert' qui a un effet similaire à 'a' si ce n'est que le texte est entré avant la ligne courante au lieu de après.
>>> 1i
# Source https://www.gnu.org/fun/jokes/ed.msg
.
Entrer une adresse sans commande nous place à cette position.
>>> 2
Note the consistent user interface and error reporting.
Nous pouvons maintenant écrire le fichier sur le disque (write donc 'w') et quitter ('q') l'éditeur. L'opération est suffisamment commune pour qu'on puisse utiliser les deux commandes en une :
>>> wq
203
Le nombre inscrit en sortie correspond au nombre d'octets écrits dans le fichier. Write peut également prendre en argument un nom de fichier pour enregistrer tout ou partie (avec une plage d'adresse) du fichier courant dans un autre.
Éditer un fichier existant
Nous allons maintenant le reprendre, Ed nous placera naturellement à la dernière ligne.
$ ed myfile
203
>>> p
yet prudent enough not to overwhelm the novice with verbosity.
On retrouve l'affichage de la taille du fichier. Afficher tout le fichier est possible à l'aide d'une plage, '$' signifiant dans ce contexte « dernière ligne ».
>>> 1,$p
# Source https://www.gnu.org/fun/jokes/ed.msg
Note the consistent user interface and error reporting.
Ed is generous enough to flag errors,
yet prudent enough not to overwhelm the novice with verbosity.
Cependant cela peut poser problème si le fichier que nous sommes en train de manipuler est particulièrement gros. Ouvrons à fin d'exemple le fichier /etc/services à l'aide de la commande 'e' pour 'edit' :
>>> e /etc/services
293694
Nous allons afficher le texte bloc par bloc depuis le début en utilisant '.' pour référencer la ligne courante.
>>> 1
# Full data: /usr/share/iana-etc/port-numbers.iana
>>> .,.+5p
# Full data: /usr/share/iana-etc/port-numbers.iana
tcpmux 1/tcp
tcpmux 1/udp
compressnet 2/tcp
compressnet 2/udp
>>> .,.+5p
compressnet 2/tcp
compressnet 3/udp
rje 5/tcp
rje 5/udp
echo 7/tcp
On voit vite plusieurs problèmes avec cette méthode :
Comme la ligne courante est comprise il faudrait en fait faire .+1,.+5p
Le fait que Ed n'ai pas d'historique des commandes rend l'opération vite lassante.
C'est en partie pour ces raisons qu'Ed permet d'afficher le texte bloc par bloc où chaque bloc fait la taille de l'écran courant avec 'z'.
>>> 1z
# Full data: /usr/share/iana-etc/port-numbers.iana
tcpmux 1/tcp
tcpmux 1/udp
compressnet 2/tcp
compressnet 2/udp
compressnet 3/tcp
compressnet 3/udp
rje 5/tcp
rje 5/udp
echo 7/tcp
echo 7/udp
discard 9/tcp
discard 9/udp
discard 9/sctp
discard 9/dccp
systat 11/tcp
systat 11/udp
daytime 13/tcp
Celui-ci peut même être combiné avec 'n' pour obtenir les numéros de lignes, et sans adresse explicite il part de la ligne courante ce qui rend la navigation plus aisée et sans répétition de ligne.
>>> zn
20 daytime 13/udp
21 qotd 17/tcp
22 qotd 17/udp
23 chargen 19/tcp
24 chargen 19/udp
25 ftp-data 20/tcp
26 ftp-data 20/udp
27 ftp-data 20/sctp
28 ftp 21/tcp
29 ftp 21/udp
30 ftp 21/sctp
31 ssh 22/tcp
32 ssh 22/udp
33 ssh 22/sctp
34 telnet 23/tcp
35 telnet 23/udp
36 smtp 25/tcp
37 smtp 25/udp
38 nsw-fe 27/tcp
Chercher et modifier
Chercher du texte est sans suprise pour un utilisateur de vim :
'/regex/' ammène à la première occurence rencontrée dans le sens de lecture naturel.
'?regex?' ammène à la première occurence rencontrée dans le sens de lecture inverse.
'/' ou '?' répètent la dernière recherche dans un sens ou dans l'autre.
Il n'est pas nécessaire de "fermer" la recherche en répétant la commande si on ne combine pas celle-ci avec autre chose.
>>> /https/
https 443/tcp
>>> ?ssh?
ssh 22/sctp
>>> /https
https 443/tcp
>>> /
https 443/udp
>>> ?
https 443/tcp
Comme suggéré on peut en effet l'employer en conjonction avec d'autres commandes car une recherche par regex est en fait considérée comme une adresse. On peut donc également les utiliser dans des plages :
>>> 1
# Full data: /usr/share/iana-etc/port-numbers.iana
>>> /ssh/n
31 ssh 22/tcp
>>> /ssh/,/ssh/+5p
ssh 22/tcp
ssh 22/udp
ssh 22/sctp
telnet 23/tcp
telnet 23/udp
smtp 25/tcp
Pour réaliser une opération sur toutes les lignes correspondant à la recherche on peut utiliser le préfix 'g' pour 'global'. C'est d'ailleurs de là que vient le nom de grep : « g/re/p »
>>> g/ssh/p
ssh 22/tcp
ssh 22/udp
ssh 22/sctp
sshell 614/tcp
sshell 614/udp
netconf-ssh 830/tcp
netconf-ssh 830/udp
sdo-ssh 3897/tcp
sdo-ssh 3897/udp
netconf-ch-ssh 4334/tcp
snmpssh 5161/tcp
snmpssh-trap 5162/tcp
tl1-ssh 6252/tcp
tl1-ssh 6252/udp
ssh-mgmt 17235/tcp
ssh-mgmt 17235/udp
Pour réaliser une commande sur les lignes ne correspondant pas à une regex on utilise le préfixe 'v' à la place de 'g' qui a notamment donné son nom à l'option de grep '-v'.
Modifier le texte se fait principalement par l'intermédiare de deux commandes : 's' pour 'substitute' et 'c' pour 'change'. La première modifie une ou plusieurs lignes en remplaçant une expression par une autre alors que la seconde supprime la ou les lignes addressées et entre en mode insertion afin d'entrer la nouvelle version.
Substitute ne change cependant que la première occurence par défaut. On peut lui adjoindre le suffixe 'g' pour signifier toutes les occurences ou en suffixe un nombre pour signifier l'occurence précise à remplacer. De plus, omettre le symbole de fin de commande indique la volonté d'afficher le nouveau texte :
>>> 1
# Full data: /usr/share/iana-etc/port-numbers.iana
>>> s/a/A/
>>> p
# Full dAta: /usr/share/iana-etc/port-numbers.iana
>>> s/a/A
# Full dAtA: /usr/share/iana-etc/port-numbers.iana
>>> s/a/A/2
>>> p
# Full dAtA: /usr/share/iAna-etc/port-numbers.iana
>>> s/a/A/2p
# Full dAtA: /usr/share/iAnA-etc/port-numbers.iana
>>> s/a/A/g
>>> p
# Full dAtA: /usr/shAre/iAnA-etc/port-numbers.iAnA
>>> s/A/a/gp
# Full data: /usr/share/iana-etc/port-numbers.iana
Pour substituer sur l'ensemble du texte on peut soit utiliser la plage '1,$' soit le préfixe '%' qui a le même effet. On est placé à la dernière occurence modifiée.
>>> 1,$s/a/A/gp
mAtAhAri 49000/tcp
>>> %s/A/a/gp
matahari 49000/tcp
On peut annuler la denrière modification avec 'u' pour 'undo'. Refaire 'u' annule l'annulation ce qui revient à appliquer de nouveau la modification.
>>> u
>>> p
mAtAhAri 49000/tcp
>>> u
>>> p
matahari 49000/tcp
Supprimer une ligne se fait avec 'd' pour 'delete'.
>>> 1,5n
1 # Full data: /usr/share/iana-etc/port-numbers.iana
2
3 tcpmux 1/tcp
4 tcpmux 1/udp
5 compressnet 2/tcp
>>> 3d
>>> 1,5n
1 # Full data: /usr/share/iana-etc/port-numbers.iana
2
3 tcpmux 1/udp
4 compressnet 2/tcp
5 compressnet 2/udp
>>> u
>>> 1,5n
1 # Full data: /usr/share/iana-etc/port-numbers.iana
2
3 tcpmux 1/tcp
4 tcpmux 1/udp
5 compressnet 2/tcp
Pour supprimer et insérer on peut utiliser 'c' :
>>> 3,4c
haha
huhu
.
>>> 1,5n
1 # Full data: /usr/share/iana-etc/port-numbers.iana
2
3 haha
4 huhu
5 compressnet 2/tcp
Puisque nous n'avons pas les droits d'écriture dans le fichier il nous faut quitter sans enregistrer, c'est fait avec 'Q'.
>>> w
/etc/services: Permission denied
?
Cannot open output file
>>> q
?
Warning: buffer modified
>>> Q
Ce qui a été vu jusqu'ici suffit à une utilisation des plus basiques mais il y a plus à Ed que cela et le confort dépend beaucoup des petites choses qui facilitent la vie sans forcément la changer.
Utilisation avancée
Interagir avec le shell
Les interactions avec le shell sont sans doute le point le plus important du confort dans l'utilisation de Ed. Elle se fait avec la commande '!' et permet soit d'exécuter une commande shell, soit d'en récupérer le contenu dans le document courant, soit de passer tout ou partie du document courant comme entrée standard d'une commande.
$ ed
>>> !ls
articles bin Documents downloads images myfile usr videos
!
>>> r !ls
58
>>> 1zn
1 articles
2 bin
3 Documents
4 downloads
5 images
6 myfile
7 usr
8 videos
>>> 2,5w !grep o
Documents
downloads
31
>>> w !tr 'A-Z' 'a-z'
articles
bin
documents
downloads
images
myfile
usr
videos
58
>>> q
On a ici utilisé la commande 'r' pour 'read' que l'on avait pas eu l'occasion de croiser auparavant. Elle permet d'insérer à la position courante le contenu d'un autre fichier ou, comme ici avec '!', d'une commande shell.
Insérer du texte au début d'un bloc
Moins une fonctionnalité avancée qu'un « truc » utile pour, par exemple commenter ou indenter du texte:
$ ed
>>> a
My best pony is Pinkie Pie
Although I like Twilight very much
And Rainbow Dash is just the coolest
.
>>> 1,3s/^/# /
>>> 1,3p
# My best pony is Pinkie Pie
# Although I like Twilight very much
# And Rainbow Dash is just the coolest
Déplacer et copier
Déplacer un bloc se fait avec 'm' pour 'move' et copier se fait avec 't' pour 'transfer'. Le bloc est placé après la ligne à l'adresse indiquée, celle-ci pouvant être 0.
$ ed myfile
203
>>> 1zn
1 # Source https://www.gnu.org/fun/jokes/ed.msg
2 Note the consistent user interface and error reporting.
3 Ed is generous enough to flag errors,
4 yet prudent enough not to overwhelm the novice with verbosity.
>>> 1,2m3
>>> 1zn
1 Ed is generous enough to flag errors,
2 # Source https://www.gnu.org/fun/jokes/ed.msg
3 Note the consistent user interface and error reporting.
4 yet prudent enough not to overwhelm the novice with verbosity.
>>> 2,3t0
>>> 1zn
1 # Source https://www.gnu.org/fun/jokes/ed.msg
2 Note the consistent user interface and error reporting.
3 Ed is generous enough to flag errors,
4 # Source https://www.gnu.org/fun/jokes/ed.msg
5 Note the consistent user interface and error reporting.
6 yet prudent enough not to overwhelm the novice with verbosity.
Copier peut également se faire avec 'y' pour 'yank' et 'x'. Yank place la cible dans le buffer de copie et 'x' place le contenu du buffer de copie à l'adresse indiquée. Yank n'est pas la seule commande à modifier ce buffer : lorsque l'on supprime avec 'd' ou change avec 'c' une ou plusieurs lignes elles sont également placées dans ce buffer ce qui permet de faire du « couper/coller ».
>>> 3y
>>> 5x
>>> 1zn
1 # Source https://www.gnu.org/fun/jokes/ed.msg
2 Note the consistent user interface and error reporting.
3 Ed is generous enough to flag errors,
4 # Source https://www.gnu.org/fun/jokes/ed.msg
5 Note the consistent user interface and error reporting.
6 Ed is generous enough to flag errors,
7 yet prudent enough not to overwhelm the novice with verbosity.
>>> 7d
>>> 3x
>>> 1zn
1 # Source https://www.gnu.org/fun/jokes/ed.msg
2 Note the consistent user interface and error reporting.
3 Ed is generous enough to flag errors,
4 yet prudent enough not to overwhelm the novice with verbosity.
5 # Source https://www.gnu.org/fun/jokes/ed.msg
6 Note the consistent user interface and error reporting.
7 Ed is generous enough to flag errors,
Ressources externes
- Cheat Sheet
http://www.catonmat.net/download/ed.text.editor.cheat.sheet.pdf
- Tutorial par le vénérale Brian Kernighan (co-créateur d'unix et du C)
http://www.verticalsysadmin.com/vi/a_tutorial_introduction_to_the_unix_text_editor.pdf