В следующей программе, если я устанавливаю переменную $foo
в значение 1 внутри первого if
оператора, это работает в том смысле, что ее значение запоминается после оператора if. Однако, когда я устанавливаю ту же переменную в значение 2 внутри оператора, if
который находится внутри while
оператора, он забывается после while
цикла. Он ведет себя так, будто я использую какую-то копию переменной $foo
внутри while
цикла, и я изменяю только эту конкретную копию. Вот полная тестовая программа:
#!/bin/bash
set -e
set -u
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to 1: $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
echo "Value of \$foo in while loop body: $foo"
done
echo "Variable \$foo after while loop: $foo"
# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1
# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
bash
while-loop
scope
sh
Эрик Лиля
источник
источник
SC2030: Modification of foo is local (to subshell caused by pipeline).
Ответы:
while
Цикл выполняется в подоболочках. Поэтому любые изменения, внесенные в переменную, не будут доступны после выхода из подоболочки.Вместо этого вы можете использовать строку here, чтобы переписать цикл while для включения в основной процесс оболочки; только
echo -e $lines
будет работать в подоболочке:Вы можете избавиться от довольно некрасивого
echo
в приведенной выше строке, расширив последовательности обратной косой черты сразу при назначенииlines
.$'...'
Форма цитирования может использоваться там:источник
<<< "$(echo -e "$lines")"
на простой<<< "$lines"
tail -f
вместо фиксированного текста?while read -r line; do echo "LINE: $line"; done < <(tail -f file)
(очевидно, что цикл не завершится, так как он продолжает ждать ввода отtail
).while
циклом илиfor
циклом; скорее использование subshell, т. е. incmd1 | cmd2
,cmd2
находится в subshell. Таким образом, еслиfor
цикл выполняется в подоболочке, будет показано непредвиденное / проблемное поведение.ОБНОВЛЕНИЕ # 2
Объяснение в ответе Голубых Лун.
Альтернативные решения:
Устранить
echo
Добавьте эхо внутри документа "здесь и есть"
Запустите
echo
в фоновом режиме:Перенаправить на дескриптор файла явно (запомните пробел в
< <
!):Или просто перенаправить на
stdin
:И один за
chepner
(устранениеecho
):Переменная
$lines
может быть преобразована в массив без запуска новой вложенной оболочки. Символы\
иn
должны быть преобразованы в некоторый символ (например, реальный символ новой строки) и использовать переменную IFS (Внутренний разделитель полей), чтобы разбить строку на элементы массива. Это можно сделать так:Результат
источник
lines
единственной целью переменной является подачаwhile
цикла.for line in $(echo -e $lines); do ... done
Вы являетесь 742342-м пользователем, чтобы задать этот часто задаваемый вопрос bash. Ответ также описывает общий случай переменных, установленных в подоболочках, созданных каналами:
источник
Хммм ... Я бы почти поклялся, что это сработало для оригинальной оболочки Bourne, но сейчас у меня нет доступа к работающей копии, чтобы проверить.
Однако существует очень тривиальное решение этой проблемы.
Измените первую строку скрипта с:
в
И вуаля! Чтение в конце конвейера работает нормально, если у вас установлена оболочка Korn.
источник
Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и читается внутри цикла как 1.
Или используйте метод heredoc, чтобы уменьшить количество кода в подоболочке. Обратите внимание, что итеративное значение i можно прочитать вне цикла while.
источник
Как насчет очень простого метода
источник
Это интересный вопрос, затрагивающий очень простую концепцию оболочки Bourne и subshell. Здесь я предлагаю решение, которое отличается от предыдущих решений тем, что выполняет какую-то фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки того, что загруженные файлы соответствуют известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показывает только 3 строки):
Сценарий оболочки:
Родитель и subshell взаимодействуют с помощью команды echo. Вы можете выбрать несколько простых для анализа текста для родительской оболочки. Этот метод не нарушает ваш обычный образ мышления, просто вы должны выполнить некоторую постобработку. Для этого вы можете использовать grep, sed, awk и другие.
источник