Piping bash string manipulation

9

J'ai lu d'autres questions sur la manipulation de chaînes bash de piping mais elles semblent être des applications spécialisées.

Essentiellement, existe-t-il un moyen de faire le plus simple ci-dessous?

au lieu de

$ string='hello world'; string2="${string// /_}"; echo "${string2^^}"
HELLO_WORLD

quelque chose comme

$ echo 'hello world' | $"{-// /_}" | "${ -^^}"
HELLO_WORLD

Edit Je suis intéressé à rester dans les manipulations bash si possible pour maintenir la vitesse (par opposition à sed / awk qui ont tendance à ralentir considérablement mes scripts)

Edit2: @jimmij

J'aime le deuxième exemple et m'a amené à faire une fonction.

bash_m() { { read x; echo "${x// /_}"; } | { read x; echo "${x^^}"; }; }
echo hello world | bash_m
HELLO_WORLD
Miati
la source
1
pourquoi pensez-vous que sed / awk serait lent à cet effet? Ils sont aussi rapides qu'ils viennent.
mkc
1
@Ketan Sed et awk sont des processus séparés, ils ne peuvent donc jamais être rapides comme quelque chose que bash peut faire nativement sans lancer un processus séparé. Normalement, cette différence est à peine perceptible, mais lorsque les performances sont importantes dans les scripts shell, c'est généralement une certaine boucle ou le calcul est répété un très grand nombre de fois, et générer des milliers de processus sera sensiblement plus lent que de faire une simple manipulation de chaîne en bash nativement.
jw013
2
@ jw013 Cela est vrai pour les chaînes courtes comme "bonjour le monde" de la question, mais si la chaîne est très longue, disons le trmanuel, alors l'inverse est vrai parce que le temps d'apparition des processus est négligeable par rapport au temps de manipulation de chaîne pour lequel sedet awksont dédiés. Si la chaîne est extrêmement longue, disons tout le manuel de bash, alors bash peut simplement refuser de continuer, en raison de certaines limitations internes.
jimmij
2
@ jw013 Je prétend que le code de manipulation de chaîne de bash est moins efficace alors que des outils dédiés sed, awk, trou similaires. Regardez la réponse de gena2x, que j'ai modifiée il y a quelque temps en ajoutant exactement ces informations: unix.stackexchange.com/questions/162221/… vous voudrez peut-être la comparer avec la réponse de terdon à la même question où il donne du temps pour les chaînes courtes dans lesquelles le processus de traitement des cas prend le plus de temps. Vous pouvez le tester vous-même et publier le résultat.
jimmij
1
@Miati Pourquoi pensez-vous que cet extra read x; echo $xest meilleur pour la performance? La syntaxe ne semble ni plus courte ni plus propre. x=${x// /_}; x=${x^^}est une manière beaucoup plus concise de faire la même chose que {read x; echo ${x.... En ce qui concerne les performances, @jimmij a souligné que tr/ sedserait plus rapide que bash, le nombre de fourches étant égal. L'utilisation d'un tuyau entraîne toujours un processus supplémentaire, donc l'argument de l'enregistrement d'une fourche ne s'applique plus. Ainsi, si vous utilisez des pipes, utilisez simplement sed/ tretc. Si vous pouvez le faire en bash, faites-le et sautez ce read x; echo $xnon - sens.
jw013

Réponses:

9

Ce que jimmij a dit. Son dernier exemple est le plus proche que vous pouvez obtenir de ce que vous tentez dans votre expression canalisée.

Voici une variante sur ce thème:

echo 'hello world'|echo $(read s;s=${s^^};echo ${s// /_})

Je serais enclin à l'utiliser tr, car c'est assez rapide.

echo 'hello world'|tr ' [:lower:]' '_[:upper:]'

Je suppose que c'est dommage que bash ne permette pas l'expansion de paramètres imbriqués; OTOH, l'utilisation de telles expressions imbriquées pourrait facilement conduire à un code difficile à lire. À moins que vous ayez vraiment besoin que les choses tournent le plus vite possible, il vaut mieux écrire du code facile à lire, à comprendre et à maintenir, plutôt que du code intelligent qui est un PITA à déboguer. Et si vous avez vraiment faire les choses doivent être faites à toute vitesse , vous devez utiliser le code compilé, pas un script.

PM 2Ring
la source
7

Vous ne pouvez pas transmettre des extensions de paramètres de cette manière. Lorsque vous vous référez à l' xutilisation du $symbole comme dans le "${x}"formulaire, il doit s'agir d'un vrai nom de variable, pas d'une entrée standard, du moins pas dans bash. Dans, zshvous pouvez effectuer des substitutions de paramètres imbriqués de la manière suivante:

$ x=''hello world'
$ echo ${${x// /_}:u}
HELLO_WORLD

(note: :uest pour zshle même que ^^pour bash)

L'imbrication dans bash n'est pas possible et je pense que ce que vous avez écrit en question est le meilleur que vous puissiez obtenir, mais si pour une raison étrange vous devez impliquer des tuyaux dans l'équation, vous pouvez essayer ceci:

$ echo 'hello world' | { read x; echo "${x// /_}"; } | { read y; echo "${y^^}"; }
HELLO_WORLD
jimmij
la source
1
Compte tenu de notre récente conversation sur la façon dont tr/ sedsont plus rapides qu'au bashtraitement des chaînes et sur la façon dont vous utilisez les canaux pour passer les chaînes via les E / S standard, je vois littéralement zéro point de faire ces opérations en bash par opposition à tr/ sed. Pourquoi serait-on jamais | { read x; echo $x... }opposé à un | sedqui fait la même chose?
jw013
1
@ jw013 franchement je vois à peu près aucun point. Ce n'est qu'un exemple pour impliquer de force les tuyaux dans le problème, car OP les a explicitement demandés et ne voulait pas utiliser de programmes externes (les deux echoet readsont bash intégrés, donc en principe un peu plus rapide). Comme je l'ai déjà écrit dans la réponse, les manipulations progressives des paramètres que OP a dans la question est la meilleure que l'on puisse obtenir à mon avis pour cette tâche en bash. Quoi qu'il en soit, le problème est plutôt académique.
jimmij