web-dev-qa-db-fra.com

Bash: attribuer la sortie du tuyau à une variable

J'essaie d'obtenir la sortie d'un tuyau dans une variable. J'ai essayé les choses suivantes:

echo foo | myvar=$(</dev/stdin)
echo foo | myvar=$(cat)
echo foo | myvar=$(tee)

Mais $myvar est vide.

Je ne veux pas faire:

myvar=$(echo foo)

Parce que je ne veux pas engendrer de sous-shell.

Des idées?

Edit: Je ne veux pas générer un sous-shell car la commande avant le canal doit modifier les variables globales, ce qu'elle ne peut pas faire dans un sous-shell. Peut-il? La chose echo est juste pour simplifier. C'est plus comme:

complex_function | myvar=$(</dev/stdin)

Et je ne comprends pas, pourquoi cela ne fonctionne pas. Cela fonctionne par exemple:

complex_function | echo $(</dev/stdin)
9
Parckwart

Utilisez la substitution de commande:

myvar=`echo foo`

ou

myvar=$(echo foo)
8
Zumo de Vidrio

La bonne solution consiste à utiliser la substitution de commandes comme ceci:

variable=$(complex_command)

un péché

message=$(echo 'hello')

(ou d'ailleurs, message=hello dans ce cas).

Votre pipeline:

echo 'hello' | message=$(</dev/stdin)

ou

echo 'hello' | read message

fonctionne réellement. Le seul problème est que le shell que vous utilisez exécutera la deuxième partie du pipeline dans un sous-shell. Ce sous-shell est détruit à la sortie du pipeline, donc la valeur de $message n'est pas conservé dans Shell.

Ici, vous pouvez voir que cela fonctionne:

$ echo 'hello' | { read message; echo "$message" }
hello

... mais comme l'environnement du sous-shell est séparé (et disparu):

$ echo "$message"

(aucune sortie)

Une solution pour vous serait de passer à ksh93 ce qui est plus intelligent à ce sujet:

$ echo 'hello' | read message
$ echo "$message"
hello

Une autre solution pour bash serait de définir l'option lastpipe Shell. Cela ferait fonctionner la dernière partie du pipeline dans l'environnement actuel. Cependant, cela ne fonctionne pas dans les shells interactifs car lastpipe nécessite que le contrôle des travaux ne soit pas actif.

#!/bin/bash

shopt -s lastpipe
echo 'hello' | read message
echo "$message"
8
Kusalananda

Dans ksh93, vous pouvez utiliser:

var=${
  my command that updates global variable
}

C'est une forme de substitution de commande qui ne génère pas de sous-shell. Pour les commandes qui sont des commandes intégrées, au lieu de les faire écrire leur sortie dans un canal (pour lequel vous auriez besoin de différents processus pour lire et écrire sur le canal pour éviter les blocages), ksh93 ne leur fait simplement pas sortir quoi que ce soit, mais rassembler ce qu'ils auraient dû produire pour constituer l'extension.

$ ksh -c 'a=${ b=123; echo foo;}; echo "$a $b"'
foo 123

La substitution de commandes de fish se comporte également comme ceci:

$ fish -c 'set a (set b 123; echo foo); echo $a $b'
foo 123

Dans la plupart des autres shells, vous utiliseriez un fichier temporaire:

my command that updates global variable > file
var=$(cat file) # can be optimised to $(<file) with some shells

Sous Linux et avec bash ou zsh (qui utilisent des fichiers temporaires pour <<<), tu peux faire:

{ my command that updates global variable > /dev/fd/3 &&
  var=$(cat<&3); } 3<<< ''
3