Question

Quelqu'un peut-il expliquer ce comportement ?En cours d'exécution:

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

n'entraîne aucune sortie, tandis que :

#!/bin/sh
echo "hello world" > test.file
read var1 var2 < test.file
echo $var1
echo $var2

produit le résultat attendu :

hello
world

Le canal ne devrait-il pas faire en une seule étape ce que la redirection vers test.file a fait dans le deuxième exemple ?J'ai essayé le même code avec les shells dash et bash et j'ai obtenu le même comportement de la part des deux.

Était-ce utile?

La solution

Un ajout récent à bash est le lastpipe , qui permet à la dernière commande d'un pipeline de s'exécuter dans le shell actuel, et non dans un sous-shell, lorsque le contrôle des tâches est désactivé.

#!/bin/bash
set +m      # Deactiveate job control
shopt -s lastpipe
echo "hello world" | read var1 var2
echo $var1
echo $var2

produira en effet

hello
world

Autres conseils

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

ne produit aucune sortie car les pipelines exécutent chacun de leurs composants dans un sous-shell.Sous-coquilles hériter de copies des variables du shell parent, plutôt que de les partager.Essaye ça:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
(
    echo $foo
    foo="foo contents modified"
    echo $foo
)
echo $foo

Les parenthèses définissent une région de code qui est exécutée dans un sous-shell, et $foo conserve sa valeur d'origine après avoir été modifié à l'intérieur.

Maintenant, essayez ceci :

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
{
    echo $foo
    foo="foo contents modified"
    echo $foo
}
echo $foo

Les accolades sont uniquement destinées au regroupement, aucun sous-shell n'est créé et le $foo modifié à l'intérieur des accolades est le même $foo modifié à l'extérieur d'elles.

Maintenant, essayez ceci :

#!/bin/sh
echo "hello world" | {
    read var1 var2
    echo $var1
    echo $var2
}
echo $var1
echo $var2

À l'intérieur des accolades, la fonction read intégrée crée correctement $var1 et $var2 et vous pouvez voir qu'elles sont répercutées.En dehors des appareils orthodontiques, ils n'existent plus.Tout le code entre accolades a été exécuté dans un sous-shell parce que c'est un composant d'un pipeline.

Vous pouvez placer des quantités arbitraires de code entre accolades, vous pouvez donc utiliser cette construction de canalisation dans un bloc chaque fois que vous avez besoin d'exécuter un bloc de script shell qui analyse la sortie de quelque chose d'autre.

Cette question a déjà reçu une réponse correcte, mais la solution n'a pas encore été indiquée.Utilisez ksh, pas bash.Comparer:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | bash -s

À:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | ksh -s
hello
world

ksh est un shell de programmation supérieur en raison de petites subtilités comme celle-ci.(bash est le meilleur shell interactif, à mon avis.)

read var1 var2 < <(echo "hello world")

Le message a reçu une réponse correcte, mais j'aimerais proposer une alternative qui pourrait peut-être être utile.

Pour attribuer des valeurs séparées par des espaces de echo (ou stdout d'ailleurs) aux variables shell, vous pouvez envisager d'utiliser des tableaux shell :

$ var=( $( echo 'hello world' ) )
$ echo ${var[0]}
hello
$ echo ${var[1]}
world

Dans cet exemple, var est un tableau et son contenu est accessible à l'aide de la construction ${var[index]}, où index est l'index du tableau (commence par 0).

De cette façon, vous pouvez attribuer autant de paramètres que vous le souhaitez à l’index du tableau concerné.

Mon point de vue sur ce problème (en utilisant Bash) :

read var1 var2 <<< "hello world"
echo $var1 $var2

Très bien, j'ai compris !

C'est un bug difficile à détecter, mais il résulte de la façon dont les tuyaux sont gérés par le shell.Chaque élément d'un pipeline s'exécute dans un processus distinct.Lorsque la commande read définit var1 et var2, elle leur définit son propre sous-shell, et non le shell parent.Ainsi, lorsque le sous-shell se termine, les valeurs de var1 et var2 sont perdues.Vous pouvez cependant essayer de faire

var1=$(echo "Hello")
echo var1

qui renvoie la réponse attendue.Malheureusement, cela ne fonctionne que pour des variables uniques, vous ne pouvez pas en définir plusieurs à la fois.Afin de définir plusieurs variables à la fois, vous devez soit lire une variable et la découper en plusieurs variables, soit utiliser quelque chose comme ceci :

set -- $(echo "Hello World")
var1="$1" var2="$2"
echo $var1
echo $var2

Même si j'admets que ce n'est pas aussi élégant que d'utiliser une pipe, ça marche.Bien sûr, vous devez garder à l'esprit que read était destiné à lire des fichiers dans des variables, donc le lire à partir d'une entrée standard devrait être un peu plus difficile.

C'est parce que la version pipe crée un sous-shell, qui lit la variable dans son espace local qui est ensuite détruit à la sortie du sous-shell.

Exécutez cette commande

$ echo $$;cat | read a
10637

et utilisez pstree -p pour examiner les processus en cours, vous verrez un shell supplémentaire accroché à votre shell principal.

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)

Essayer:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )

Le problème, comme plusieurs personnes l'ont déclaré, est que var1 et var2 sont créés dans un environnement de sous-shell qui est détruit à la fermeture de ce sous-shell.Ce qui précède évite de détruire le sous-shell jusqu'à ce que le résultat ait été renvoyé.Une autre solution est :

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top