Переменная, измененная внутри цикла while, не запоминается

#arrays #bash #while-loop #pipe #subshell

Вопрос:

В следующей программе, если я установлю переменной $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 linensecond linenthird 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)
 

Комментарии:

1.Чтение ключей здесь: Я задаю переменные в цикле, который находится в конвейере. Почему они исчезают после завершения цикла? Или, почему я не могу передавать данные по конвейеру для чтения?.

2. Утилита shellcheck улавливает это (см. github.com/koalaman/shellcheck/wiki/SC2030 ); Вырезать и вставить приведенный выше код в shellcheck.net выдает этот отзыв для строки 19: SC2030: Modification of foo is local (to subshell caused by pipeline).

Ответ №1:

 echo -e $lines | while read line 
    ...
done
 

while Цикл выполняется в подсеточной оболочке. Таким образом, любые изменения, которые вы внесете в переменную, будут недоступны после выхода из вложенной оболочки.

Вместо этого вы можете использовать строку here для перезаписи цикла while, чтобы он находился в основном процессе оболочки; 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 -e "$lines")"
 

Вы можете избавиться от довольно уродливого echo в приведенной выше строке, развернув последовательности обратной косой черты сразу при назначении lines . $'...' Форма цитирования может быть использована там:

 lines=


Комментарии:

1. лучше перейти <<< "$(echo -e "$lines")" на простой <<< "$lines"

2. что делать, если источник был взят tail -f вместо фиксированного текста?

3. @mteee Вы можете использовать while read -r line; do echo "LINE: $line"; done < <(tail -f file) (очевидно, что цикл не завершится, так как он продолжает ждать ввода от tail ).

4. Я ограничен /bin/sh - есть ли альтернативный способ, который работает в старой оболочке?

5. @AvinashYadav Проблема на самом деле не связана с while циклом или for циклом; скорее, использование подоболочки, т. Е. в cmd1 | cmd2 , cmd2 находится в подоболочке. Таким образом, если for цикл выполняется во вложенной оболочке, будет показано неожиданное/проблемное поведение.

Ответ №2:

ОБНОВЛЕНО#2

Объяснение содержится в ответе Blue Moons.

Альтернативные решения:

Устранить echo

 while read line; do
...
done <<EOT
first line
second line
third line
EOT
 

Добавьте эхо внутри вот-этого-документа

 while read line; do
...
done <<EOT
$(echo -e $lines)
EOT
 

Запуск echo в фоновом режиме:

 coproc echo -e $lines
while read -u ${COPROC[0]} line; do 
...
done
 

Перенаправление на дескриптор файла явно (обратите внимание на пробел < < !):

 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 (Разделитель внутренних полей) для разделения строки на элементы массива. Это можно сделать так:

 lines="first linensecond linenthird line"
echo "$lines"
OIFS="$IFS"
IFS=

Результат таков

 first linensecond linenthird line
first line second line third line, Length: 3
arr=([0]="first line" [1]="second line" [2]="third line")
 

Комментарии:

1. 1 для here-документа, так lines как единственная цель переменной, по-видимому, состоит в том, чтобы запустить while цикл.

2. @чепнер: Спасибо! Я добавил еще один, посвященный Тебе!

3. Здесь приведено еще одно решение: for line in $(echo -e $lines); do ... done

4. @dma_k Спасибо за ваш комментарий! Это решение привело бы к 6 строкам, содержащим одно слово. Запрос ОП был другим...

5. голосовали за. запуск echo в подсетке внутри здесь-это было одним из немногих решений, которые работали в ash

Ответ №3:

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

E4) Если я передаю вывод команды в read variable , почему вывод не отображается при $variable завершении команды чтения?

Это связано с отношениями "родители-потомки" между процессами Unix. Это влияет на все команды, выполняемые в конвейерах, а не только на простые вызовы read . Например, передача выходных данных команды в while цикл, который многократно вызывается read , приведет к одному и тому же поведению.

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

Многие конвейеры, которые заканчиваются, read variable могут быть преобразованы в замены команд, которые будут захватывать выходные данные указанной команды. Затем выходные данные могут быть присвоены переменной:

 grep ^gnu /usr/lib/news/active | wc -l | read ngroup
 

может быть преобразован в

 ngroup=$(grep ^gnu /usr/lib/news/active | wc -l)
 

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

Скажем, /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))
 

Комментарии:

1. Ты забыл о проблеме Семейной вражды . Иногда очень трудно найти ту же комбинацию слов, что и тот, кто написал ответ, чтобы вас не завалили неправильными результатами и не отфильтровали указанный ответ.

Ответ №4:

Хммм... Я бы почти поклялся, что это сработало для оригинальной оболочки Борна, но у меня сейчас нет доступа к запущенной копии, чтобы проверить.

Однако существует очень тривиальное решение этой проблемы.

Измените первую строку скрипта с:

 #!/bin/bash
 

Для

 #!/bin/ksh
 

Et voila! Чтение в конце конвейера работает просто отлично, если у вас установлена оболочка Korn.

Ответ №5:

Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и считывается внутри цикла как 1.

 # reading lines of content from 2 files concatenated
# inside loop: write value of var i to stderr (before iteration)
# outside: read var i from stderr, has last iterative value

f=/tmp/file1
g=/tmp/file2
i=1
cat $f $g | 
while read -r s;
do
  echo $s > /dev/null;  # some work
  echo $i > 2
  let i  
done;
read -r i < 2
echo $i
 

Или используйте метод heredoc, чтобы уменьшить объем кода в подоболочке.
Обратите внимание, что итеративное значение i может быть считано вне цикла while.

 i=1
while read -r s;
do
  echo $s > /dev/null
  let i  
done <<EOT
$(cat $f $g)
EOT
let i--
echo $i
 

Комментарии:

1. Очень полезно. В цикле while может быть несколько переменных, и каждая из них может передаваться на другой номер echo $i > 2; echo $j > 3 . Затем вы можете перенаправить каждую из них обратно в глобальные переменные после цикла while read -r i < 2; read -r j < 3 .

Ответ №6:

Это интересный вопрос, и он затрагивает очень базовую концепцию в оболочке и подоболочке Борна. Здесь я предлагаю решение, которое отличается от предыдущих решений, выполнив некоторую фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки соответствия загруженных файлов известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показано всего 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 amp;amp; 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 и многое другое.

Ответ №7:

Как насчет очень простого метода

      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 linensecond linenthird 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.
 

Комментарии:

1. Возможно, здесь есть достойный ответ под поверхностью, но вы сократили форматирование до такой степени, что неприятно пытаться читать.

2. Вы имеете в виду, что исходный код приятно читать? (Я только что последовал :p)

Ответ №8:

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

Используйте echo инструкцию для хранения и cat инструкцию для извлечения. И пользователь bash должен chown иметь доступ к каталогу или chmod доступ для чтения и записи.

 #write to file
echo "1" > foo.txt

while condition; do 
    if (condition); then
        #write again to file
        echo "2" > foo.txt      
    fi
done

#read from file
echo "Value of $foo in while loop body: $(cat foo.txt)"
 

Комментарии:

1. Ты пробовал это сделать? xxx=0; while true; do if true; then xxx=100 ; fi; break; done; echo $xxx

first linensecond linenthird line'
while read line; do
...
done <<< "$lines"

Комментарии:

1. лучше перейти <<< "$(echo -e "$lines")" на простой <<< "$lines"

2. что делать, если источник был взят tail -f вместо фиксированного текста?

3. @mteee Вы можете использовать while read -r line; do echo "LINE: $line"; done < <(tail -f file) (очевидно, что цикл не завершится, так как он продолжает ждать ввода от tail ).

4. Я ограничен /bin/sh - есть ли альтернативный способ, который работает в старой оболочке?

5. @AvinashYadav Проблема на самом деле не связана с while циклом или for циклом; скорее, использование подоболочки, т. Е. в cmd1 | cmd2 , cmd2 находится в подоболочке. Таким образом, если for цикл выполняется во вложенной оболочке, будет показано неожиданное/проблемное поведение.

Ответ №2:

ОБНОВЛЕНО#2

Объяснение содержится в ответе Blue Moons.

Альтернативные решения:

Устранить echo


Добавьте эхо внутри вот-этого-документа


Запуск echo в фоновом режиме:


Перенаправление на дескриптор файла явно (обратите внимание на пробел < < !):


Или просто перенаправить на stdin :


И один для chepner (устранения echo ):


Переменная $lines может быть преобразована в массив без запуска новой вложенной оболочки. Символы и n должны быть преобразованы в некоторый символ (например, реальный символ новой строки) и использовать переменную IFS (Разделитель внутренних полей) для разделения строки на элементы массива. Это можно сделать так:


Результат таков


Комментарии:

1. 1 для here-документа, так lines как единственная цель переменной, по-видимому, состоит в том, чтобы запустить while цикл.

2. @чепнер: Спасибо! Я добавил еще один, посвященный Тебе!

3. Здесь приведено еще одно решение: for line in $(echo -e $lines); do ... done

4. @dma_k Спасибо за ваш комментарий! Это решение привело бы к 6 строкам, содержащим одно слово. Запрос ОП был другим...

5. голосовали за. запуск echo в подсетке внутри здесь-это было одним из немногих решений, которые работали в ash

Ответ №3:

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

E4) Если я передаю вывод команды в read variable , почему вывод не отображается при $variable завершении команды чтения?

Это связано с отношениями "родители-потомки" между процессами Unix. Это влияет на все команды, выполняемые в конвейерах, а не только на простые вызовы read . Например, передача выходных данных команды в while цикл, который многократно вызывается read , приведет к одному и тому же поведению.

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

Многие конвейеры, которые заканчиваются, read variable могут быть преобразованы в замены команд, которые будут захватывать выходные данные указанной команды. Затем выходные данные могут быть присвоены переменной:


может быть преобразован в


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

Скажем, /usr/local/bin/ipaddr-это следующий сценарий оболочки:


Вместо использования


чтобы разбить IP-адрес локальной машины на отдельные октеты, используйте


Однако имейте в виду, что это изменит позиционные параметры оболочки
. Если они вам нужны, вы должны сохранить их, прежде чем делать
это.

Это общий подход-в большинстве случаев вам не нужно будет устанавливать другое значение $IFS.

Некоторые другие предоставляемые пользователем альтернативы включают:


и, где возможна замена процесса,


Комментарии:

1. Ты забыл о проблеме Семейной вражды . Иногда очень трудно найти ту же комбинацию слов, что и тот, кто написал ответ, чтобы вас не завалили неправильными результатами и не отфильтровали указанный ответ.

Ответ №4:

Хммм... Я бы почти поклялся, что это сработало для оригинальной оболочки Борна, но у меня сейчас нет доступа к запущенной копии, чтобы проверить.

Однако существует очень тривиальное решение этой проблемы.

Измените первую строку скрипта с:


Для


Et voila! Чтение в конце конвейера работает просто отлично, если у вас установлена оболочка Korn.

Ответ №5:

Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и считывается внутри цикла как 1.


Или используйте метод heredoc, чтобы уменьшить объем кода в подоболочке.
Обратите внимание, что итеративное значение i может быть считано вне цикла while.


Комментарии:

1. Очень полезно. В цикле while может быть несколько переменных, и каждая из них может передаваться на другой номер echo $i > 2; echo $j > 3 . Затем вы можете перенаправить каждую из них обратно в глобальные переменные после цикла while read -r i < 2; read -r j < 3 .

Ответ №6:

Это интересный вопрос, и он затрагивает очень базовую концепцию в оболочке и подоболочке Борна. Здесь я предлагаю решение, которое отличается от предыдущих решений, выполнив некоторую фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки соответствия загруженных файлов известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показано всего 3 строки):


Сценарий оболочки:


Родительская и дочерняя оболочки взаимодействуют с помощью команды echo. Вы можете выбрать какой-нибудь простой для разбора текст для родительской оболочки. Этот метод не нарушает ваш обычный образ мышления, просто вам нужно выполнить некоторую постобработку. Для этого вы можете использовать grep, sed, awk и многое другое.

Ответ №7:

Как насчет очень простого метода


Комментарии:

1. Возможно, здесь есть достойный ответ под поверхностью, но вы сократили форматирование до такой степени, что неприятно пытаться читать.

2. Вы имеете в виду, что исходный код приятно читать? (Я только что последовал :p)

Ответ №8:

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

Используйте echo инструкцию для хранения и cat инструкцию для извлечения. И пользователь bash должен chown иметь доступ к каталогу или chmod доступ для чтения и записи.


Комментарии:

1. Ты пробовал это сделать? xxx=0; while true; do if true; then xxx=100 ; fi; break; done; echo $xxx

n' arr=(${lines//\n/

Результат таков


Комментарии:

1. 1 для here-документа, так lines как единственная цель переменной, по-видимому, состоит в том, чтобы запустить while цикл.

2. @чепнер: Спасибо! Я добавил еще один, посвященный Тебе!

3. Здесь приведено еще одно решение: for line in $(echo -e $lines); do ... done

4. @dma_k Спасибо за ваш комментарий! Это решение привело бы к 6 строкам, содержащим одно слово. Запрос ОП был другим...

5. голосовали за. запуск echo в подсетке внутри здесь-это было одним из немногих решений, которые работали в ash

Ответ №3:

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

E4) Если я передаю вывод команды в read variable , почему вывод не отображается при $variable завершении команды чтения?

Это связано с отношениями "родители-потомки" между процессами Unix. Это влияет на все команды, выполняемые в конвейерах, а не только на простые вызовы read . Например, передача выходных данных команды в while цикл, который многократно вызывается read , приведет к одному и тому же поведению.

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

Многие конвейеры, которые заканчиваются, read variable могут быть преобразованы в замены команд, которые будут захватывать выходные данные указанной команды. Затем выходные данные могут быть присвоены переменной:


может быть преобразован в


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

Скажем, /usr/local/bin/ipaddr-это следующий сценарий оболочки:


Вместо использования


чтобы разбить IP-адрес локальной машины на отдельные октеты, используйте


Однако имейте в виду, что это изменит позиционные параметры оболочки
. Если они вам нужны, вы должны сохранить их, прежде чем делать
это.

Это общий подход-в большинстве случаев вам не нужно будет устанавливать другое значение $IFS.

Некоторые другие предоставляемые пользователем альтернативы включают:


и, где возможна замена процесса,


Комментарии:

1. Ты забыл о проблеме Семейной вражды . Иногда очень трудно найти ту же комбинацию слов, что и тот, кто написал ответ, чтобы вас не завалили неправильными результатами и не отфильтровали указанный ответ.

Ответ №4:

Хммм... Я бы почти поклялся, что это сработало для оригинальной оболочки Борна, но у меня сейчас нет доступа к запущенной копии, чтобы проверить.

Однако существует очень тривиальное решение этой проблемы.

Измените первую строку скрипта с:


Для


Et voila! Чтение в конце конвейера работает просто отлично, если у вас установлена оболочка Korn.

Ответ №5:

Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и считывается внутри цикла как 1.


Или используйте метод heredoc, чтобы уменьшить объем кода в подоболочке.
Обратите внимание, что итеративное значение i может быть считано вне цикла while.


Комментарии:

1. Очень полезно. В цикле while может быть несколько переменных, и каждая из них может передаваться на другой номер echo $i > 2; echo $j > 3 . Затем вы можете перенаправить каждую из них обратно в глобальные переменные после цикла while read -r i < 2; read -r j < 3 .

Ответ №6:

Это интересный вопрос, и он затрагивает очень базовую концепцию в оболочке и подоболочке Борна. Здесь я предлагаю решение, которое отличается от предыдущих решений, выполнив некоторую фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки соответствия загруженных файлов известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показано всего 3 строки):


Сценарий оболочки:


Родительская и дочерняя оболочки взаимодействуют с помощью команды echo. Вы можете выбрать какой-нибудь простой для разбора текст для родительской оболочки. Этот метод не нарушает ваш обычный образ мышления, просто вам нужно выполнить некоторую постобработку. Для этого вы можете использовать grep, sed, awk и многое другое.

Ответ №7:

Как насчет очень простого метода


Комментарии:

1. Возможно, здесь есть достойный ответ под поверхностью, но вы сократили форматирование до такой степени, что неприятно пытаться читать.

2. Вы имеете в виду, что исходный код приятно читать? (Я только что последовал :p)

Ответ №8:

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

Используйте echo инструкцию для хранения и cat инструкцию для извлечения. И пользователь bash должен chown иметь доступ к каталогу или chmod доступ для чтения и записи.


Комментарии:

1. Ты пробовал это сделать? xxx=0; while true; do if true; then xxx=100 ; fi; break; done; echo $xxx

first linensecond linenthird line'
while read line; do
...
done <<< "$lines"

Комментарии:

1. лучше перейти <<< "$(echo -e "$lines")" на простой <<< "$lines"

2. что делать, если источник был взят tail -f вместо фиксированного текста?

3. @mteee Вы можете использовать while read -r line; do echo "LINE: $line"; done < <(tail -f file) (очевидно, что цикл не завершится, так как он продолжает ждать ввода от tail ).

4. Я ограничен /bin/sh — есть ли альтернативный способ, который работает в старой оболочке?

5. @AvinashYadav Проблема на самом деле не связана с while циклом или for циклом; скорее, использование подоболочки, т. Е. в cmd1 | cmd2 , cmd2 находится в подоболочке. Таким образом, если for цикл выполняется во вложенной оболочке, будет показано неожиданное/проблемное поведение.

Ответ №2:

ОБНОВЛЕНО#2

Объяснение содержится в ответе Blue Moons.

Альтернативные решения:

Устранить echo


Добавьте эхо внутри вот-этого-документа


Запуск echo в фоновом режиме:


Перенаправление на дескриптор файла явно (обратите внимание на пробел < < !):


Или просто перенаправить на stdin :


И один для chepner (устранения echo ):


Переменная $lines может быть преобразована в массив без запуска новой вложенной оболочки. Символы и n должны быть преобразованы в некоторый символ (например, реальный символ новой строки) и использовать переменную IFS (Разделитель внутренних полей) для разделения строки на элементы массива. Это можно сделать так:


Результат таков


Комментарии:

1. 1 для here-документа, так lines как единственная цель переменной, по-видимому, состоит в том, чтобы запустить while цикл.

2. @чепнер: Спасибо! Я добавил еще один, посвященный Тебе!

3. Здесь приведено еще одно решение: for line in $(echo -e $lines); do ... done

4. @dma_k Спасибо за ваш комментарий! Это решение привело бы к 6 строкам, содержащим одно слово. Запрос ОП был другим…

5. голосовали за. запуск echo в подсетке внутри здесь-это было одним из немногих решений, которые работали в ash

Ответ №3:

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

E4) Если я передаю вывод команды в read variable , почему вывод не отображается при $variable завершении команды чтения?

Это связано с отношениями «родители-потомки» между процессами Unix. Это влияет на все команды, выполняемые в конвейерах, а не только на простые вызовы read . Например, передача выходных данных команды в while цикл, который многократно вызывается read , приведет к одному и тому же поведению.

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

Многие конвейеры, которые заканчиваются, read variable могут быть преобразованы в замены команд, которые будут захватывать выходные данные указанной команды. Затем выходные данные могут быть присвоены переменной:


может быть преобразован в


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

Скажем, /usr/local/bin/ipaddr-это следующий сценарий оболочки:


Вместо использования


чтобы разбить IP-адрес локальной машины на отдельные октеты, используйте


Однако имейте в виду, что это изменит позиционные параметры оболочки
. Если они вам нужны, вы должны сохранить их, прежде чем делать
это.

Это общий подход-в большинстве случаев вам не нужно будет устанавливать другое значение $IFS.

Некоторые другие предоставляемые пользователем альтернативы включают:


и, где возможна замена процесса,


Комментарии:

1. Ты забыл о проблеме Семейной вражды . Иногда очень трудно найти ту же комбинацию слов, что и тот, кто написал ответ, чтобы вас не завалили неправильными результатами и не отфильтровали указанный ответ.

Ответ №4:

Хммм… Я бы почти поклялся, что это сработало для оригинальной оболочки Борна, но у меня сейчас нет доступа к запущенной копии, чтобы проверить.

Однако существует очень тривиальное решение этой проблемы.

Измените первую строку скрипта с:


Для


Et voila! Чтение в конце конвейера работает просто отлично, если у вас установлена оболочка Korn.

Ответ №5:

Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и считывается внутри цикла как 1.


Или используйте метод heredoc, чтобы уменьшить объем кода в подоболочке.
Обратите внимание, что итеративное значение i может быть считано вне цикла while.


Комментарии:

1. Очень полезно. В цикле while может быть несколько переменных, и каждая из них может передаваться на другой номер echo $i > 2; echo $j > 3 . Затем вы можете перенаправить каждую из них обратно в глобальные переменные после цикла while read -r i < 2; read -r j < 3 .

Ответ №6:

Это интересный вопрос, и он затрагивает очень базовую концепцию в оболочке и подоболочке Борна. Здесь я предлагаю решение, которое отличается от предыдущих решений, выполнив некоторую фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки соответствия загруженных файлов известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показано всего 3 строки):


Сценарий оболочки:


Родительская и дочерняя оболочки взаимодействуют с помощью команды echo. Вы можете выбрать какой-нибудь простой для разбора текст для родительской оболочки. Этот метод не нарушает ваш обычный образ мышления, просто вам нужно выполнить некоторую постобработку. Для этого вы можете использовать grep, sed, awk и многое другое.

Ответ №7:

Как насчет очень простого метода


Комментарии:

1. Возможно, здесь есть достойный ответ под поверхностью, но вы сократили форматирование до такой степени, что неприятно пытаться читать.

2. Вы имеете в виду, что исходный код приятно читать? (Я только что последовал :p)

Ответ №8:

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

Используйте echo инструкцию для хранения и cat инструкцию для извлечения. И пользователь bash должен chown иметь доступ к каталогу или chmod доступ для чтения и записи.


Комментарии:

1. Ты пробовал это сделать? xxx=0; while true; do if true; then xxx=100 ; fi; break; done; echo $xxx

n’}) # Conversion
IFS=»$OIFS»
echo «${arr[@]}», Length: ${#arr[*]}
set|grep ^arrРезультат таков


Комментарии:

1. 1 для here-документа, так lines как единственная цель переменной, по-видимому, состоит в том, чтобы запустить while цикл.

2. @чепнер: Спасибо! Я добавил еще один, посвященный Тебе!

3. Здесь приведено еще одно решение: for line in $(echo -e $lines); do ... done

4. @dma_k Спасибо за ваш комментарий! Это решение привело бы к 6 строкам, содержащим одно слово. Запрос ОП был другим…

5. голосовали за. запуск echo в подсетке внутри здесь-это было одним из немногих решений, которые работали в ash

Ответ №3:

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

E4) Если я передаю вывод команды в read variable , почему вывод не отображается при $variable завершении команды чтения?

Это связано с отношениями «родители-потомки» между процессами Unix. Это влияет на все команды, выполняемые в конвейерах, а не только на простые вызовы read . Например, передача выходных данных команды в while цикл, который многократно вызывается read , приведет к одному и тому же поведению.

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

Многие конвейеры, которые заканчиваются, read variable могут быть преобразованы в замены команд, которые будут захватывать выходные данные указанной команды. Затем выходные данные могут быть присвоены переменной:


может быть преобразован в


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

Скажем, /usr/local/bin/ipaddr-это следующий сценарий оболочки:


Вместо использования


чтобы разбить IP-адрес локальной машины на отдельные октеты, используйте


Однако имейте в виду, что это изменит позиционные параметры оболочки
. Если они вам нужны, вы должны сохранить их, прежде чем делать
это.

Это общий подход-в большинстве случаев вам не нужно будет устанавливать другое значение $IFS.

Некоторые другие предоставляемые пользователем альтернативы включают:


и, где возможна замена процесса,


Комментарии:

1. Ты забыл о проблеме Семейной вражды . Иногда очень трудно найти ту же комбинацию слов, что и тот, кто написал ответ, чтобы вас не завалили неправильными результатами и не отфильтровали указанный ответ.

Ответ №4:

Хммм… Я бы почти поклялся, что это сработало для оригинальной оболочки Борна, но у меня сейчас нет доступа к запущенной копии, чтобы проверить.

Однако существует очень тривиальное решение этой проблемы.

Измените первую строку скрипта с:


Для


Et voila! Чтение в конце конвейера работает просто отлично, если у вас установлена оболочка Korn.

Ответ №5:

Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и считывается внутри цикла как 1.


Или используйте метод heredoc, чтобы уменьшить объем кода в подоболочке.
Обратите внимание, что итеративное значение i может быть считано вне цикла while.


Комментарии:

1. Очень полезно. В цикле while может быть несколько переменных, и каждая из них может передаваться на другой номер echo $i > 2; echo $j > 3 . Затем вы можете перенаправить каждую из них обратно в глобальные переменные после цикла while read -r i < 2; read -r j < 3 .

Ответ №6:

Это интересный вопрос, и он затрагивает очень базовую концепцию в оболочке и подоболочке Борна. Здесь я предлагаю решение, которое отличается от предыдущих решений, выполнив некоторую фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки соответствия загруженных файлов известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показано всего 3 строки):


Сценарий оболочки:


Родительская и дочерняя оболочки взаимодействуют с помощью команды echo. Вы можете выбрать какой-нибудь простой для разбора текст для родительской оболочки. Этот метод не нарушает ваш обычный образ мышления, просто вам нужно выполнить некоторую постобработку. Для этого вы можете использовать grep, sed, awk и многое другое.

Ответ №7:

Как насчет очень простого метода


Комментарии:

1. Возможно, здесь есть достойный ответ под поверхностью, но вы сократили форматирование до такой степени, что неприятно пытаться читать.

2. Вы имеете в виду, что исходный код приятно читать? (Я только что последовал :p)

Ответ №8:

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

Используйте echo инструкцию для хранения и cat инструкцию для извлечения. И пользователь bash должен chown иметь доступ к каталогу или chmod доступ для чтения и записи.


Комментарии:

1. Ты пробовал это сделать? xxx=0; while true; do if true; then xxx=100 ; fi; break; done; echo $xxx

first linensecond linenthird line’
while read line; do

done <<< «$lines»

Комментарии:

1. лучше перейти <<< "$(echo -e "$lines")" на простой <<< "$lines"

2. что делать, если источник был взят tail -f вместо фиксированного текста?

3. @mteee Вы можете использовать while read -r line; do echo "LINE: $line"; done < <(tail -f file) (очевидно, что цикл не завершится, так как он продолжает ждать ввода от tail ).

4. Я ограничен /bin/sh — есть ли альтернативный способ, который работает в старой оболочке?

5. @AvinashYadav Проблема на самом деле не связана с while циклом или for циклом; скорее, использование подоболочки, т. Е. в cmd1 | cmd2 , cmd2 находится в подоболочке. Таким образом, если for цикл выполняется во вложенной оболочке, будет показано неожиданное/проблемное поведение.

Ответ №2:

ОБНОВЛЕНО#2

Объяснение содержится в ответе Blue Moons.

Альтернативные решения:

Устранить echo


Добавьте эхо внутри вот-этого-документа


Запуск echo в фоновом режиме:


Перенаправление на дескриптор файла явно (обратите внимание на пробел < < !):


Или просто перенаправить на stdin :


И один для chepner (устранения echo ):


Переменная $lines может быть преобразована в массив без запуска новой вложенной оболочки. Символы и n должны быть преобразованы в некоторый символ (например, реальный символ новой строки) и использовать переменную IFS (Разделитель внутренних полей) для разделения строки на элементы массива. Это можно сделать так:


Результат таков


Комментарии:

1. 1 для here-документа, так lines как единственная цель переменной, по-видимому, состоит в том, чтобы запустить while цикл.

2. @чепнер: Спасибо! Я добавил еще один, посвященный Тебе!

3. Здесь приведено еще одно решение: for line in $(echo -e $lines); do ... done

4. @dma_k Спасибо за ваш комментарий! Это решение привело бы к 6 строкам, содержащим одно слово. Запрос ОП был другим…

5. голосовали за. запуск echo в подсетке внутри здесь-это было одним из немногих решений, которые работали в ash

Ответ №3:

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

E4) Если я передаю вывод команды в read variable , почему вывод не отображается при $variable завершении команды чтения?

Это связано с отношениями «родители-потомки» между процессами Unix. Это влияет на все команды, выполняемые в конвейерах, а не только на простые вызовы read . Например, передача выходных данных команды в while цикл, который многократно вызывается read , приведет к одному и тому же поведению.

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

Многие конвейеры, которые заканчиваются, read variable могут быть преобразованы в замены команд, которые будут захватывать выходные данные указанной команды. Затем выходные данные могут быть присвоены переменной:


может быть преобразован в


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

Скажем, /usr/local/bin/ipaddr-это следующий сценарий оболочки:


Вместо использования


чтобы разбить IP-адрес локальной машины на отдельные октеты, используйте


Однако имейте в виду, что это изменит позиционные параметры оболочки
. Если они вам нужны, вы должны сохранить их, прежде чем делать
это.

Это общий подход-в большинстве случаев вам не нужно будет устанавливать другое значение $IFS.

Некоторые другие предоставляемые пользователем альтернативы включают:


и, где возможна замена процесса,


Комментарии:

1. Ты забыл о проблеме Семейной вражды . Иногда очень трудно найти ту же комбинацию слов, что и тот, кто написал ответ, чтобы вас не завалили неправильными результатами и не отфильтровали указанный ответ.

Ответ №4:

Хммм… Я бы почти поклялся, что это сработало для оригинальной оболочки Борна, но у меня сейчас нет доступа к запущенной копии, чтобы проверить.

Однако существует очень тривиальное решение этой проблемы.

Измените первую строку скрипта с:


Для


Et voila! Чтение в конце конвейера работает просто отлично, если у вас установлена оболочка Korn.

Ответ №5:

Я использую stderr для хранения внутри цикла и чтения из него снаружи. Здесь var i изначально устанавливается и считывается внутри цикла как 1.


Или используйте метод heredoc, чтобы уменьшить объем кода в подоболочке.
Обратите внимание, что итеративное значение i может быть считано вне цикла while.


Комментарии:

1. Очень полезно. В цикле while может быть несколько переменных, и каждая из них может передаваться на другой номер echo $i > 2; echo $j > 3 . Затем вы можете перенаправить каждую из них обратно в глобальные переменные после цикла while read -r i < 2; read -r j < 3 .

Ответ №6:

Это интересный вопрос, и он затрагивает очень базовую концепцию в оболочке и подоболочке Борна. Здесь я предлагаю решение, которое отличается от предыдущих решений, выполнив некоторую фильтрацию. Я приведу пример, который может быть полезен в реальной жизни. Это фрагмент для проверки соответствия загруженных файлов известной контрольной сумме. Файл контрольной суммы выглядит следующим образом (показано всего 3 строки):


Сценарий оболочки:


Родительская и дочерняя оболочки взаимодействуют с помощью команды echo. Вы можете выбрать какой-нибудь простой для разбора текст для родительской оболочки. Этот метод не нарушает ваш обычный образ мышления, просто вам нужно выполнить некоторую постобработку. Для этого вы можете использовать grep, sed, awk и многое другое.

Ответ №7:

Как насчет очень простого метода


Комментарии:

1. Возможно, здесь есть достойный ответ под поверхностью, но вы сократили форматирование до такой степени, что неприятно пытаться читать.

2. Вы имеете в виду, что исходный код приятно читать? (Я только что последовал :p)

Ответ №8:

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

Используйте echo инструкцию для хранения и cat инструкцию для извлечения. И пользователь bash должен chown иметь доступ к каталогу или chmod доступ для чтения и записи.


Комментарии:

1. Ты пробовал это сделать? xxx=0; while true; do if true; then xxx=100 ; fi; break; done; echo $xxx