Сначала вам нужно выполнить его.
Кроме того, это издевательский способ сделать это. Было бы лучше начать транзакцию, сделать SELECT, а затем определить, что делать (INSERT или UPDATE). Просто проверить, не удалось ли выполнить запрос UPDATE, это не удается, если ни одна строка не найдена.
Вы пользователь 742342nd задаете этот FAQ bash. Ответ также описывает общий случай переменных, заданных в подоболочках, созданных трубами:
E4) Если я передаю вывод команды в
read variable
, то почему вывод не появляется в$variable
при завершении команды чтения?Это связано с тем, parent-child между процессами Unix. Он затрагивает все команды, выполняемые в конвейерах, а не просто вызовы
read
. Например, соединение вывода команды в циклеwhile
, которое повторно вызываетread
, приведет к такому же поведению.Каждый элемент конвейера, даже встроенная или оболочечная функция, запускается в отдельном process, дочерний элемент оболочки, запускающей конвейер. Подпроцесс не может повлиять на среду своего родителя. Когда команда
read
устанавливает переменную на вход, эта переменная устанавливается только в подоболочке, а не в родительской оболочке. Когда подоболочка выходит, значение переменной теряется.Многие конвейеры, которые заканчиваются на
read variable
, могут быть преобразованы в подстановки команд, которые будут захватывать вывод указанной команды. Затем результат может быть назначен переменной:grep ^gnu /usr/lib/news/active | wc -l | read ngroup
можно преобразовать в
ngroup=$(grep ^gnu /usr/lib/news/active | wc -l)
Это, к сожалению, не работает, чтобы разделить текст между несколькими переменными, так как read, когда задано несколько переменных аргументов. Если вам нужно это сделать, вы можете либо использовать подстановку выше, чтобы прочитать выход в переменную, и отрубить переменную с помощью операторов расширения удаления шаблонов bash или использовать какой-то вариант следующего подхода.
Say / usr / local / bin / ipaddr - это следующий сценарий оболочки:
#! /bin/sh host `hostname` | awk '/address/ {print $NF}'
Вместо использования
/usr/local/bin/ipaddr | read A B C D
, чтобы разбить IP-адрес локального компьютера на отдельные октеты, используйте
OIFS="$IFS" IFS=. set -- $(/usr/local/bin/ipaddr) IFS="$OIFS" A="$1" B="$2" C="$3" D="$4"
Остерегайтесь, однако, что это изменит позиционные параметры оболочки. Если вам это нужно, вы должны сохранить их, прежде чем делать это.
Это общий подход - в большинстве случаев вам не нужно устанавливать $ IFS на другое значение.
Некоторые другие пользовательские альтернативы включают в себя:
read A B C D << HERE $(IFS=.; echo $(/usr/local/bin/ipaddr)) HERE
и, где доступна подстановка процесса,
read A B C D < <(IFS=.; echo $(/usr/local/bin/ipaddr))
Хммм ... Я бы почти поклялся, что это сработало для оригинальной оболочки 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
Добавить эхо внутри документа here-is-document
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
):
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")
lines
, похоже, является подача петли while
.
– chepner
31 May 2013 в 13:44
for line in $(echo -e $lines); do ... done
– dma_k
19 January 2016 в 21:20
<<< "$(echo -e "$lines")"
до простого<<< "$lines"
– beliy 18 May 2017 в 14:43