Необязательные перенаправления ввода сценариев оболочки
Может ли кто-нибудь объяснить это поведение?
Продолжительность:
#!/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 и получил одинаковое поведение от обоих.
Ответы
Ответ 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