2008-08-05 5 views
16

Может ли кто-нибудь объяснить это поведение? Продолжительность:Смещение сценариев перенаправления ввода

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

приводит ничего будучи вывод, в то время как:

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

производит ожидаемый результат:

hello 
world 

не должен трубы сделать один шаг, что перенаправление test.file сделал во втором примере? Я попробовал один и тот же код с оболочкой тире и bash и получил одинаковое поведение от обоих.

ответ

8

Недавнее дополнение к bash является lastpipe вариант, который позволяет последней команды в конвейере для запуска в текущей оболочке, а не подоболочка, когда управление заданиями деактивировано.

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

будет действительно выход

hello 
world 
3

Это потому, что версия для труб создает подоболочку, которая считывает переменную в ее локальное пространство, которое затем уничтожается при выходе из оболочки.

Выполнить эту команду

$ echo $$;cat | read a 
10637 

и использовать pstree -p, чтобы посмотреть на запущенные процессы, вы увидите дополнительные оболочки висит от вашей основной оболочки.

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

Хорошо, я это понял!

Это трудная ошибка, которая может быть уловлена, но получается, что трубы обрабатываются оболочкой. Каждый элемент конвейера работает в отдельном процессе. Когда команда чтения устанавливает var1 и var2, задает им свою собственную подоболочку, а не родительскую оболочку. Поэтому, когда подоболочка выходит, значения var1 и var2 теряются. Можно, однако, попытаться сделать

var1=$(echo "Hello") 
echo var1 

который возвращает ожидаемый ответ. К сожалению, это работает только для отдельных переменных, вы не можете устанавливать много за раз. Для того, чтобы установить несколько переменных в то время, вы должны либо прочитать в одной переменной и нарезать его на несколько переменных или использовать что-то вроде этого:

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

Хотя я признаю, что это не так элегантно, как с помощью трубы, она работает , Конечно, вы должны помнить, что чтение предназначалось для чтения из файлов в переменные, поэтому чтение его со стандартного ввода должно быть немного сложнее.

+2

Это зависит от выбора оболочки. Ksh93 был разработан для ограничения производственных расходов.Он также запускает конечный элемент конвейера * внутри * вызывающего процесса оболочки, тем самым сохраняя состояние. – 2012-07-26 09:49:52

+1

Bash 4.2 представила возможность сделать то же самое. Отключите управление заданиями (`set + m`) и установите опцию` lastpipe` (`shopt -s lastpipe`). – chepner 2012-07-26 12:10:02

+0

Я займусь этим. Благодаря! – 2012-07-31 14:32:51

9

Это был дан правильный ответ, но решение еще не указано. Используйте ksh, а не bash. Сравните:

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

To:

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

КШ является превосходным программированием оболочки из-за маленькие тонкости, как это. (bash - лучшая интерактивная оболочка, на мой взгляд.)

3

Сообщение было должным образом ответственно, но я хотел бы предложить альтернативный один вкладыш, который, возможно, может быть полезен.

Для присвоения пространства разделенных значений от эха (или стандартный вывода по этому вопросу) в оболочку переменных, вы могли бы рассмотреть вопрос об использовании массивов оболочки:

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

В этом примере варе массив и содержимое можно получить с помощью конструкцию $ {var [index]}, где index - индекс массива (начинается с 0).

Таким образом, у вас может быть столько параметров, сколько вы хотите присвоить соответствующему индексу массива.

3

Try:

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

Проблема, как несколько человек заявили, что var1 и var2 создаются в подоболочек среде, которая разрушается, когда это подоболочка выходы. Вышеизложенное позволяет избежать разрушения подоболочки до тех пор, пока результат не будет эхо. Другим решением является:

result=`echo "hello world"` 
read var1 var2 <<EOF 
$result 
EOF 
echo $var1 
echo $var2 
10
#!/bin/sh 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

не производит никакого вывода, поскольку проходят трубопроводы каждого из их компонентов внутри субоболочке. Подголовки наследуют копии переменных родительской оболочки, а не разделяют их. Попробуйте это:

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

Скобок определяет область кода, запускаемую в субоболочке и $ Foo сохраняет свое исходное значение после того, как изменяется внутри них.

Теперь попробуйте это:

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

Скобки чисто для группировки, не подоболочка не создается, и $ Foo модифицирована в фигурных скобках те же $ Foo модифицирована за их пределами.

Теперь попробуйте это:

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

Внутри скобок чтение создает встроенные $ var1 и $ var2 правильно, и вы можете видеть, что они получают эхо. Вне брекетов они больше не существуют. Весь код в фигурных скобках был запущен в подоболочке , потому что это один из компонентов конвейера.

Вы можете поместить произвольное количество кода между фигурными скобками, чтобы вы могли использовать эту конструкцию трубопроводов в блок, когда вам нужно запустить блок сценария оболочки, который анализирует вывод чего-то еще.

4

Мое мнение по этому вопросу (с помощью Bash):

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