Сведения о вопросе

Sadijon

16:03, 1st July, 2020

Теги

linux   bash   shell   ksh   dash-shell    

Shell странности перенаправления ввода сценариев

Просмотров: 481   Ответов: 9

Может ли кто-нибудь объяснить такое поведение? Бегущий:

#!/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 сделало во втором примере? Я попробовал один и тот же код с оболочками dash и bash и получил одинаковое поведение от обоих из них.



  Сведения об ответе

crush

18:03, 1st July, 2020

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

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

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

hello
world


  Сведения об ответе

PROGA

18:03, 1st July, 2020

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

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

#!/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

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

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


  Сведения об ответе

padenie

18:03, 1st July, 2020

На этот вопрос уже дан правильный ответ, но решение пока не сформулировано. Используйте ksh, а не bash. Сравнивать:

$ 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-это превосходное Программирование shell из-за таких маленьких тонкостей, как эта. (На мой взгляд, bash-это лучший интерактивный shell.)


  Сведения об ответе

appple

18:03, 1st July, 2020

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


  Сведения об ответе

davran

18:03, 1st July, 2020

Мое мнение по этому вопросу (используя Bash):

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


  Сведения об ответе

LIZA

18:03, 1st July, 2020

Ну ладно, я все понял!

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

var1=$(echo "Hello")
echo var1

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

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

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


  Сведения об ответе

Chhiki

18:03, 1st July, 2020

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

Для назначения разделенных пробелами значений из echo (или stdout, если уж на то пошло) переменным shell можно использовать массивы shell:

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

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

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


  Сведения об ответе

JUST___

18:03, 1st July, 2020

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

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

$ echo $$;cat | read a
10637

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

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


  Сведения об ответе

PIRLO

18:03, 1st July, 2020

Попробуй:

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться