web-dev-qa-db-fra.com

Comment exécuter la sortie d'une commande dans le shell actuel?

Je connais bien l'utilitaire source (ou .), qui extrait le contenu d'un fichier et l'exécute dans le shell en cours.

Maintenant, je transforme du texte en commandes Shell, puis je les exécute comme suit:

$ ls | sed ... | sh

ls est juste un exemple aléatoire, le texte original peut être n'importe quoi. sed aussi, juste un exemple pour transformer du texte. Le bit intéressant est sh. J'écoute tout ce que j'ai à sh et le lance.

Mon problème est que cela signifie démarrer un nouveau sous-shell. Je préférerais que les commandes soient exécutées dans mon shell actuel. Comme je pourrais le faire avec source some-file, si j'avais les commandes dans un fichier texte.

Je ne veux pas créer de fichier temporaire car je me sens sale.

Sinon, j'aimerais démarrer mon sous-shell avec exactement les mêmes caractéristiques que mon shell actuel.

mettre à jour

Ok, les solutions utilisant backtick fonctionnent, certes, mais je dois souvent le faire pendant que je vérifie et modifie la sortie, alors je préférerais de loin s'il y avait un moyen d'intégrer le résultat dans quelque chose à la fin.

triste mise à jour

Ah, le /dev/stdin était si joli, mais dans un cas plus complexe, ça ne marchait pas.

Donc, j'ai ceci:

find . -type f -iname '*.doc' | ack -v '\.doc$' | Perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

Ce qui garantit que tous les fichiers .doc ont leur extension en minuscule.

Et qui accessoirement, peut être manipulé avec xargs, mais c’est d’autre chose.

find . -type f -iname '*.doc' | ack -v '\.doc$' | Perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

Donc, quand je lance l'ancien, il va sortir tout de suite, rien ne se passe.

73
kch
$ ls | sed ... | source /dev/stdin

UPDATE: Ceci fonctionne dans bash 4.0, ainsi que tcsh et dash (si vous changez source en .). Apparemment, c'était buggy en bash 3.2. Notes de publication de bash 4.0 :

Correction d'un bug qui causait `. ' ne pas lire ni exécuter des commandes à partir de fichiers non normaux tels que des périphériques ou des canaux nommés.

76
mark4o

La commande eval existe à cet effet même.

eval "$( ls | sed... )"

Plus du manuel bash :

eval

          eval [arguments]

Les arguments sont concaténés ensemble en une seule commande, qui est ensuite lu et exécuté, et son état de sortie renvoyé comme sortie statut de eval. S'il n'y en a pas arguments ou uniquement des arguments vides, le le statut de retour est zéro.

113
Juliano

Wow, je sais que c'est une vieille question, mais je me suis retrouvé exactement avec le même problème récemment (c'est comme ça que je suis arrivé ici).

Quoi qu'il en soit - je n'aime pas la réponse source /dev/stdin, mais je pense en avoir trouvé une meilleure. C'est faussement simple en fait:

echo ls -la | xargs xargs

Bonne droite? En fait, cela ne fait toujours pas ce que vous voulez, car si vous avez plusieurs lignes, il les concatémera en une seule commande au lieu d'exécuter chaque commande séparément. La solution que j'ai trouvée est donc:

ls | ... | xargs -L 1 xargs

l'option -L 1 signifie que vous utilisez (au plus) 1 ligne par exécution de commande. Remarque: si votre ligne se termine par un espace de fin, elle sera concaténée avec la ligne suivante! Assurez-vous donc que chaque ligne se termine par un non-espace.

Enfin, vous pouvez faire 

ls | ... | xargs -L 1 xargs -t

pour voir quelles commandes sont exécutées (-t est détaillé).

J'espère que quelqu'un lit ceci!

32
rabensky

Essayez d’utiliser process substitution , qui remplace la sortie d’une commande par un fichier temporaire pouvant ensuite être obtenu:

source <(echo id)
26
rishta

Je crois que c'est "la bonne réponse" à la question:

ls | sed ... | while read line; do $line; done

C'est-à-dire que l'on peut accéder à une boucle while; la commande read prend une ligne à partir de sa stdin et l'assigne à la variable $line. $line devient alors la commande exécutée dans la boucle; et il continue jusqu'à ce qu'il n'y ait plus de lignes dans son entrée.

Cela ne fonctionne toujours pas avec certaines structures de contrôle (comme une autre boucle), mais cela convient dans ce cas.

6
Geoff Nixon
`ls | sed ...`

J'ai le sentiment que ls | sed ... | source - serait plus joli, mais malheureusement source ne comprend pas - qui signifie stdin.

5
chaos

Je pense que votre solution consiste à remplacer les commandes par des backticks: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

Voir section 3.4.5

1
Eric Wendelin

Pour utiliser la solution de mark4o sur bash 3.2 (macos), une chaîne here peut être utilisée à la place de pipelines, comme dans cet exemple:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"
0
ish-west

Pourquoi ne pas utiliser source alors?

$ ls | sed ... > out.sh ; source out.sh
0
Kaleb Pederson