Зависит от дизайна вашей оболочки.
Соответствует ли это стандарту POSIX?Если да, то ваша оболочка должна прочитать переменную среды ENV в начале любого интерактивного сеанса и прочитать / выполнить содержимое файла (ов), к которому относится переменная ENV. Никакой другой файл не должен читаться в соответствии со стандартом. Это минимальный минимум для POSIX-конформации в отношении чтения файлов при запуске.
Соответствует ли это стандарту POSIX?
Как насчет настройки?
Хммм ... Я бы почти поклялся, что это сработало для оригинальной оболочки Bourne, но сейчас у меня нет доступа к текущей копии.
Однако есть очень тривиальный обходной путь к проблеме.
Измените первую строку скрипта:
#!/bin/bash
на
#!/bin/ksh
Et voila! Чтение в конце конвейера работает очень хорошо, если у вас установлена оболочка Korn.
Это интересный вопрос и коснуться очень простой концепции в оболочке Борна и подоболочке. Здесь я предоставляю решение, отличное от предыдущих решений, делая какую-то фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки загруженных файлов, соответствующих знанию контрольных сумм. Файл контрольной суммы выглядит следующим образом (показано всего 3 строки):
49174 36326 dna_align_feature.txt.gz
54757 1 dna.txt.gz
55409 9971 exon_transcript.txt.gz
Сценарий оболочки:
#!/bin/sh
.....
failcnt=0 # this variable is only valid in the parent shell
#variable xx captures all the outputs from the while loop
xx=$(cat ${checkfile} | while read -r line; do
num1=$(echo $line | awk '{print $1}')
num2=$(echo $line | awk '{print $2}')
fname=$(echo $line | awk '{print $3}')
if [ -f "$fname" ]; then
res=$(sum $fname)
filegood=$(sum $fname | awk -v na=$num1 -v nb=$num2 -v fn=$fname '{ if (na == $1 && nb == $2) { print "TRUE"; } else { print "FALSE"; }}')
if [ "$filegood" = "FALSE" ]; then
failcnt=$(expr $failcnt + 1) # only in subshell
echo "$fname BAD $failcnt"
fi
fi
done | tail -1) # I am only interested in the final result
# you can capture a whole bunch of texts and do further filtering
failcnt=${xx#* BAD } # I am only interested in the number
# this variable is in the parent shell
echo failcnt $failcnt
if [ $failcnt -gt 0 ]; then
echo $failcnt files failed
else
echo download successful
fi
Родитель и подоболочка взаимодействуют через команду echo. Вы можете легко выбрать текст для родительской оболочки. Этот метод не нарушает ваш нормальный образ мышления, просто нужно выполнить некоторую пост-обработку. Для этого вы можете использовать grep, sed, awk и т. Д.
Как насчет очень простого метода
+call your while loop in a function
- set your value inside (nonsense, but shows the example)
- return your value inside
+capture your value outside
+set outside
+display outside
#!/bin/bash
# set -e
# set -u
# No idea why you need this, not using here
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
function my_while_loop
{
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2; return 2;
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
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"
return 2;
fi
# Code below won't be executed since we returned from function in 'if' statement
# We aready reported the $foo var beint set to 2 anyway
echo "Value of \$foo in while loop body: $foo"
done
}
my_while_loop; foo="$?"
echo "Variable \$foo after while loop: $foo"
Output:
Setting $foo 1
Variable $foo after if statement: 1
Value of $foo in while loop body: 1
Variable $foo after while loop: 2
bash --version
GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
Copyright (C) 2007 Free Software Foundation, Inc.
ОБНОВЛЕНО # 2
Объяснение в ответе Голубого Луны.
Альтернативные решения:
Устранить echo
while read line; do
...
done <<EOT
first line
second line
third line
EOT
Добавьте эхо в документ здесь [-d5]
while read line; do
...
done <<EOT
$(echo -e $lines)
EOT
Запустите echo в фоновом режиме:
coproc echo -e $lines
while read -u ${COPROC[0]} line; do
...
done
Перенаправить на дескриптор файла явно (Mind the space в < <!):
exec 3< <(echo -e $lines)
while read -u 3 line; do
...
done
Или просто перенаправить на stdin:
while read line; do
...
done < <(echo -e $lines)
И один для chepner (исключая echo): [ ! d9]
arr=("first line" "second line" "third line");
for((i=0;i<${#arr[*]};++i)) { line=${arr[i]};
...
}
Переменная $lines может быть преобразована в массив без запуска новой под-оболочки. Символы \ и n должны быть преобразованы в некоторый символ (например, настоящий новый символ строки) и использовать переменную IFS (Internal Field Separator) для разделения строки на элементы массива. Это можно сделать так:
lines="first line\nsecond line\nthird line"
echo "$lines"
OIFS="$IFS"
IFS=$'\n' arr=(${lines//\\n/$'\n'}) # Conversion
IFS="$OIFS"
echo "${arr[@]}", Length: ${#arr[*]}
set|grep ^arr
Результат
first line\nsecond line\nthird line
first line second line third line, Length: 3
arr=([0]="first line" [1]="second line" [2]="third line")