Подтвердить что ты не робот

Необязательные перенаправления ввода сценариев оболочки

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

#!/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 и получил одинаковое поведение от обоих.

4b9b3361

Ответ 1

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

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

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

hello
world

Ответ 2

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

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

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

Ответ 3

Это уже ответили правильно, но решение еще не указано. Используйте 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

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

Ответ 4

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

Ответ 5

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

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

var1=$(echo "Hello")
echo var1

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

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

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

Ответ 6

Взять эту проблему (используя Bash):

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

Ответ 7

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

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

$ echo $$;cat | read a
10637

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

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

Ответ 8

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

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

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

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

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

Ответ 9

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