Domanda

Qualcuno può spiegare questo comportamento?Corsa:

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

non viene prodotto alcun risultato, mentre:

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

produce l'output atteso:

hello
world

La pipe non dovrebbe fare in un solo passaggio ciò che ha fatto il reindirizzamento a test.file nel secondo esempio?Ho provato lo stesso codice sia con la shell dash che con quella bash e ho ottenuto lo stesso comportamento da entrambe.

È stato utile?

Soluzione

Una recente aggiunta a bash è il lastpipe opzione, che consente l'esecuzione dell'ultimo comando in una pipeline nella shell corrente, non in una sottoshell, quando il controllo del lavoro è disattivato.

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

uscirà davvero

hello
world

Altri suggerimenti

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

non produce output perché le pipeline eseguono ciascuno dei loro componenti all'interno di una subshell.Sottoshell ereditare copie delle variabili della shell madre, invece di condividerle.Prova questo:

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

Le parentesi definiscono una regione di codice che viene eseguita in una subshell e $foo mantiene il suo valore originale dopo essere stato modificato al loro interno.

Ora prova questo:

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

Le parentesi graffe servono esclusivamente per il raggruppamento, non viene creata alcuna subshell e $foo modificato all'interno delle parentesi graffe è lo stesso $foo modificato all'esterno di esse.

Ora prova questo:

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

All'interno delle parentesi graffe, il built-in di lettura crea correttamente $var1 e $var2 e puoi vedere che vengono visualizzati in eco.Al di fuori delle parentesi graffe, non esistono più.Tutto il codice tra parentesi graffe è stato eseguito in una subshell perché è un componente di una pipeline.

Puoi inserire quantità arbitrarie di codice tra parentesi graffe, quindi puoi utilizzare questa costruzione piping in un blocco ogni volta che devi eseguire un blocco di script di shell che analizzi l'output di qualcos'altro.

A questo è già stata data una risposta corretta, ma la soluzione non è stata ancora dichiarata.Usa ksh, non bash.Confrontare:

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

A:

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

ksh è una shell di programmazione superiore grazie a piccole sottigliezze come questa.(bash è la migliore shell interattiva, secondo me.)

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

Al post è stata data una risposta adeguata, ma vorrei offrire una frase alternativa che forse potrebbe essere di qualche utilità.

Per assegnare valori separati da spazi da echo (o stdout) alle variabili di shell, potresti prendere in considerazione l'utilizzo di array di shell:

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

In questo esempio var è un array ed è possibile accedere al contenuto utilizzando il costrutto ${var[index]}, dove indice è l'indice dell'array (inizia con 0).

In questo modo puoi avere tutti i parametri che desideri assegnati al relativo indice dell'array.

La mia opinione su questo problema (usando Bash):

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

Va bene, ho capito!

Questo è un bug difficile da individuare, ma deriva dal modo in cui i pipe vengono gestiti dalla shell.Ogni elemento di una pipeline viene eseguito in un processo separato.Quando il comando di lettura imposta var1 e var2, li imposta nella propria subshell, non nella shell genitore.Pertanto, quando la subshell esce, i valori di var1 e var2 vengono persi.Puoi comunque provare a farlo

var1=$(echo "Hello")
echo var1

che restituisce la risposta attesa.Sfortunatamente questo funziona solo per variabili singole, non puoi impostarne molte alla volta.Per impostare più variabili alla volta devi leggere una variabile e suddividerla in più variabili oppure utilizzare qualcosa del genere:

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

Anche se ammetto che non è elegante come usare una pipa, funziona.Ovviamente dovresti tenere presente che read è pensato per leggere da file in variabili, quindi renderlo letto dallo standard input dovrebbe essere un po' più difficile.

È perché la versione pipe sta creando una subshell, che legge la variabile nel suo spazio locale che poi viene distrutto quando la subshell esce.

Esegui questo comando

$ echo $$;cat | read a
10637

e usa pstree -p per guardare i processi in esecuzione, vedrai una shell extra appesa alla tua shell principale.

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

Tentativo:

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

Il problema, come hanno affermato più persone, è che var1 e var2 vengono creati in un ambiente di subshell che viene distrutto quando la subshell viene chiusa.Quanto sopra evita di distruggere la subshell finché il risultato non è stato echeggiato.Un'altra soluzione è:

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top