Comment donner une liste séparée par des virgules comme arguments à la commande suivante


9

J'ai un script s1qui produit une liste de nombres séparés par ',' par exemple 1,2,3,4. Maintenant, je veux donner ces nombres au script s2comme arguments, afin que s2 soit exécuté sur chacun d'eux et produise son résultat sur une ligne distincte. Par exemple, si s2 multiplie les nombres par deux, ce serait le résultat que je recherche:

2
4
6
8

Ce que je fais en ce moment, c'est:

s1 | xargs -d "," | xargs -n1 s2

Mais j'ai l'impression de le faire d'une manière si stupide! Ma question est donc:

Quelle est la bonne façon de procéder?

Mon problème avec ma solution est qu'il appelle deux fois xargs et itère deux fois sur l'entrée, ce qui n'est pas raisonnable à mes yeux bien sûr en termes de performances! La réponse xargs -d "," -n1semble agréable, mais je ne suis pas sûr que ce soit une seule fois. Si c'est le cas, veuillez le vérifier dans votre réponse et je l'accepterai. Soit dit en passant, je préfère ne pas utiliser Perl car il répète toujours deux fois et Perl peut également ne pas exister sur de nombreux systèmes.


1
Si cela fonctionne, pourquoi l'appeler stupide. Si le temps d'exécution est important, c'est une autre question, partez d'ici
George Udosen

2
Essayez cecis1 | xargs -d "," -n1 s2
George Udosen

1
Je soupçonne qu'une partie du problème est de mal comprendre l'impact de l'itération. Itérer sur les éléments dans quelque chose comme un tableau associatif est mauvais à cause du coût de parcourir cette structure de données, mais "itérer" en général n'est pas intrinsèquement mauvais. Plus précisément, la lecture des données ligne par ligne au fur et à mesure qu'elles arrivent sur STDIN, n'est pas un énorme problème de performances. Le problème de performance ici est davantage le coût de la création d'un nouveau processus et de la configuration du pipeline. Tant que vous ne le faites pas fréquemment (comme dans une boucle), s'inquiéter des performances de xargs est probablement un exemple d'optimisation prématurée.
dannysauer

Réponses:


18

Cela devrait également fonctionner:

s1 | xargs -d "," -n1 s2

Cas de test:

printf 1,2,3,4 | xargs -d ',' -n1 echo

Résultat:

1
2
3
4

Si s1affiche cette liste suivie d'un caractère de nouvelle ligne, vous voudrez la supprimer car sinon le dernier appel serait avec 4\nau lieu de 4:

s1 | tr -d '\n' | xargs -d , -n1 s2

6

Si s2peut accepter plusieurs arguments, vous pouvez faire:

(IFS=,; ./s2 $(./s1))

qui remplace temporairement IFS pour être une virgule, le tout dans un sous-shell, de sorte que s2la sortie de soit s1décomposée par des virgules. Le sous-shell est un moyen rapide de modifier IFS sans enregistrer la valeur précédente ou la réinitialiser.

Une version précédente de cette réponse était incorrecte, probablement en raison d'un paramètre IFS résiduel, corrompant les résultats. Merci à ilkkachu d' avoir signalé mon erreur .

Pour boucler manuellement les sorties et les fournir individuellement s2, démontrez ici la sauvegarde et la réinitialisation d'IFS:

oIFS="$IFS"
IFS=,
for output in $(./s1); do ./s2 "$output"; done
IFS="$oIFS"

ou exécutez les bits IFS dans un sous-shell comme précédemment:

(
IFS=,
for output in $(./s1); do ./s2 "$output"; done
)

1
Êtes-vous sûr de ce premier? bash -c 'IFS=, printf "%s\n" $(echo 1,2,3)'imprime 1,2,3sur mon système, c'est-à-dire qu'il n'y a pas de division.
ilkkachu

Je vais re-tester un peu, mais je soupçonne un comportement différent basé sur les programmes intégrés par rapport aux programmes externes.
Jeff Schaller

même avec /usr/bin/printfet/bin/echo
ilkkachu

1
@ilkkachu Vous avez raison, le fractionnement des mots se produit avant que l'IFS ne soit réaffecté dans ce cas (IFS=,; printf "%s\n" $(echo 1,2,3)), en revanche, devrait fonctionner.
undercat applaudit Monica

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.