Comment fonctionne cette commande de substitution «sed» avec beaucoup de signes @?

8

Quelqu'un peut-il expliquer comment cette sedcommande fonctionne?

sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf "%b"
Raj
la source
3
La façon normale de procéder consiste à utiliser des barres obliques, mais cela peut devenir fastidieux si vous recherchez et remplacez quelque chose par des barres obliques. Ce n'est pas le cas ici, donc même si c'est très bien, cela déroute les futurs mainteneurs comme vous.
Thorbjørn Ravn Andersen
2
… Et les amène à apprendre quelque chose de nouveau sur sedce chemin! :)
dessert

Réponses:

15

Dans sed, les commandes de substitution sont généralement écrites sous la forme s/pattern/replacement/options. Cependant, il n'est pas nécessaire d'utiliser /- vous pouvez utiliser d'autres caractères si cela vous convient, ce pourrait donc être s@pattern@replacement@optionsou s:foo:bar:g. s@+@ @gc'est comme s/+/ /g- remplacer tout +par des espaces. De même s@%@\\x@gremplace tous %avec \x(une seule barre oblique inverse est un caractère d'échappement à sed, vous avez donc besoin de deux pour obtenir une barre oblique inverse réelle).

Une chaîne comme foo+%2Fbardeviendra alors foo \x2Fbar. printf "%b"étendra les séquences échappées par une barre oblique inverse comme \x2F(le caractère ASCII dont la valeur hexadécimale est 2F, qui est /) pour enfin vous donner foo /bar.

muru
la source
2
En bref, un décodeur URL-> nom de fichier.
Thorbjørn Ravn Andersen
10

La commande pour laquelle vous demandez le décodage des +es et des %séquences des URL n'est pas seulement une sedcommande, c'est un pipeline qui traite les entrées avec sed, puis les redirige vers xargsun traitement ultérieur. Voyons d'abord la sedcommande:

sed 's@+@ @g;s@%@\\x@g'

Vous pouvez être plus habitué à le voir avec /plutôt que @comme séparateur, ce qui aurait facilement pu être fait ici sans complication car il /n'apparaît dans aucun des modèles de recherche ni dans aucun des textes de remplacement. Cette commande est équivalente:

sed 's/+/ /g;s/%/\\x/g'

Comme /, @est un caractère de ponctuation parfaitement bon pour sed.

Sur chaque ligne d'entrée:

  1. s@+@ @g( s/+/ /g) substitue ( s) les occurrences de +avec un espace. Cela affecte tous les +es sur une ligne ( g), pas seulement le premier.

  2. ; termine l'action ("commande") et vous permet d'en spécifier une autre dans le même "script".

  3. s@%@\\x@g( s/%/\\x/g) substitue ( s) les occurrences de %avec \x. Comme précédemment, il agit sur tous plutôt que sur la première de chaque ligne ( g).

    Dans \\xle \\représente un seul \car \a une signification particulière pour sed. Sa signification spéciale est en fait le personnage que vous utilisez pour enlever la signification spéciale d'un autre personnage qui vient après lui qui aurait autrement une signification spéciale. Il faut donc échapper comme \\.


Regardons maintenant une xargscommande dont le but est de s'exécuter printf.

xargsconstruit des lignes de commande. Si vous exécutez , où est un ou plusieurs mots, s'exécute avec des arguments de ligne de commande supplémentaires lus à partir de son entrée. Dans ce cas, l'entrée de est la sortie de , en raison de pipe ( ). Interprète normalement tout espace dans son entrée pour signifier que le texte avant et après il constitue des arguments séparés, mais l' option le fait fractionner les arguments à la place du caractère nul .xargs command...command...xargscommand...xargssed|xargs-0

Dans l'utilisation prévue de votre commande, un caractère nul n'apparaîtra pas et xargss'exécutera printf %bavec un seul argument de ligne de commande supplémentaire, la sortie de la sedcommande. Ainsi, bien qu'il ne soit pas équivalent en général, dans ce cas, l'ensemble du pipeline aurait pu être écrit comme ceci en utilisant la substitution de commandes au lieu de xargs:

printf '%b\n' "$(sed 's/+/ /g;s/%/\\x/g')"

Quant à ce qui printfest prévu ici, comme le dit muru, le %bspécificateur de format consomme et imprime un argument (comme %s) mais provoque des échappements de barre oblique inverse - du type que la sedcommande sur le côté gauche du tuyau a été écrit pour générer - à traduire dans les personnages qu'ils représentent .

Supposons que j'exécute cette commande et passe http://foldoc.org/debugging%20by%20printfen entrée. J'obtiens http://foldoc.org/debugging by printfen sortie, car les %20séquences sont traduites en espaces.

Eliah Kagan
la source
3

C'est la beauté de sed, il applique ses paradigmes à lui-même ... Après la commande (comme sou trou rien), le personnage suivant est considéré comme le séparateur.

Vous devez choisir judicieusement pour éviter les interférences avec le shell et la commande elle-même, et garder la chose lisible, mais il est parfaitement valable d'écrire quelque chose d'aussi horrible que:

echo 'arrival' | sed srarbrg

... et obtenez brrivblen conséquence, ce que vous attendez. Vous pouvez vous amuser à le rendre vraiment cryptique, comme dans:

echo 'arrival' | sed s\fa\fb\fg   # \f is form feed, chr(12)

L'utilisation courante consiste à utiliser la barre oblique comme délimiteur, mais lorsque votre expression contient le délimiteur, il est plus facile de saisir l'intention. Votre délimiteur peut être n'importe quoi dans la plage ASCII8 (délimiteurs multi-octets tels que £provoquer une erreur).

N'oubliez pas que l'objectif est de rendre les choses plus faciles, pas plus cryptiques.

Marabiloso
la source
Fonctionnant avec l'idée cryptique, il s'agit d'une commande sed valide, bien qu'elle ne fasse rien d'utile:sed "snack is an apple or something" <<< "I sed your snack is an apple or something"
wjandrea
Agréable! Oui, vous pouvez également utiliser des sedcommandes comme casse-tête, comment est-ce geek?
Marabiloso