Переменные получают сброс после цикла чтения, который считывается из трубопровода

StackOverflow https://stackoverflow.com/questions/7313491

Вопрос

initiate () {
read -p "Location(s) to look for .bsp files in? " loc
find $loc -name "*.bsp" | while read
do
    if [ -f "$loc.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $loc
    fi
    if [ "$scan" == "1" ]; then bzipint $loc
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done

echo $filcount    #Reset to 0
echo $zipcount    #Reset to 0
echo $scacount    #Reset to 0
echo $valid       #Still equal to 1
}

Я пишу сценарий Bash Shell для использования bzip2 Чтобы застегнуть все .bsp Файлы внутри каталога. В этом сценарии у меня есть несколько переменных для подсчета итог (файлы, успешные ZIP, успешное сканирование целостности), однако, похоже, я столкнулся с проблемой.

Когда find $loc -name "*.bsp" Заканчивается файлами, чтобы дать while read а также while read выходит, это Zeros Out $filcount, $zipcount а также $scacount (Все это изменено (увеличено) внутри initiate (), bzip () (который называется во время initiate ()) или же bzipint () (который также называется в initiate ()).

Чтобы проверить, связано ли это с изменяющимися переменными внутри initiate () или другие функции, доступные из него, я использовал Echo $valid, который определяется вне initiate () (как $filcount, $zipcount, и т. д.), но не изменяется с другой функции внутри initiate () или внутри initiate () сам.

Интересно, $valid не получает сброс до 0, как и другие переменные внутри инициации.

Кто -нибудь может сказать мне, почему мои переменные волшебным образом получают сброс, когда во время чтения выходят?

Это было полезно?

Решение

Я столкнулся с этой проблемой вчера.

Проблема в том, что вы делаете find $loc -name "*.bsp" | while read. Анкет Потому что это включает в себя трубу, while read Loop на самом деле не может работать в том же процессе Bash, что и остальная часть вашего сценария; Bash должен вытекать из подпроцессы, чтобы он мог соединить Stdout find к stdin while петля.

Это все очень умно, но это означает, что любые переменные, установленные в цикле, нельзя увидеть после цикла, что полностью победило всю цель while петля я писал.

Вы можете либо попытаться подавать вход в цикл без использования трубы, либо получить выход из петли без использования переменных. В итоге у меня ужасная мерзость, включающая как написание во временный файл, так и обернув весь цикл в $(...), вот так:

var="$(producer | while read line; do
    ...
    echo "${something}"
done)"

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

Другие советы

Если вы используете Bash

while read
do
    if [ -f "$REPLY.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $REPLY
    fi
    if [ "$scan" == "1" ]; then bzipint $REPLY
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done < <(find $loc -name "*.bsp")

К Суммируйте варианты за с использованием read В конце [концептуального эквивалента] трубопровода в Posix-подобных снарядах:

Чтобы подтвердить: в Bash по умолчанию и в строго Posix-совместимых оболочках всегда, Все команды в трубопроводе в подборная, так переменные, которые они создают или изменяют, не будут видны для Текущий оболочка (Не существует после того, как конвейер заканчивается).

Следующее крышки bash, ksh, zsh, а также sh ([в основном] только Posix-Features Shells, такие как dash) и показывает способы избежать создания подсборки, чтобы сохранить переменные, созданные / модифицированные read.

Если минимальный номер версии не указан, предположим, что даже «довольно старые» версии поддерживают его (рассматриваемые функции существуют в течение долгого времени, но я не знаю конкретно, когда они были представлены.

Обратите внимание, что в качестве альтернативы [Posix-совместимым] решениям ниже Вы всегда можете захватить выход команды в [временном] файле, а затем подавать его read в качестве < file, что также избегает подборных.


ksh, а также zsh: Не требуется обходного пути/конфигурации вообще:

А read встроенный по умолчанию Бежит в Текущий оболочка при использовании в качестве последний командование в трубопроводе.

По-видимому, ksh а также zsh по умолчанию бежать Любые командование в последний этап трубопровода в Текущий оболочка.
Наблюдается в ksh 93u+ а также zsh 5.0.5.
Если вы знаете конкретно в какой версии этой функции, дайте мне знать.

#!/usr/bin/env ksh
#!/usr/bin/env zsh

out= # initialize output variable

# Pipe multiple lines to the `while` loop and collect the values in the output variable.
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash 4.2+: использовать lastpipe вариант оболочки

В Bash версии 4.2 или выше, включение опции оболочки lastpipe вызывает последний сегмент трубопровода в Текущий оболочка, позволяя чтению создавать переменные, видимые для текущей оболочки.

#!/usr/bin/env bash

shopt -s lastpipe # bash 4.2+: make the last pipeline command run in *current* shell

out=
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash, ksh, zsh: использовать Замена процесса

Случайно говоря, замена процесса - это способ, чтобы выходные данные команды действовали как временный файл.

out=
while read -r var; do
  out+="$var/"
done < <(printf '%s\n' one two three) # <(...) is the process substitution

echo "$out" # -> 'one/two/three'

bash, ksh, zsh: использовать здесь с командование замены

out=
while read -r var; do
  out+="$var/"
done <<< "$(printf '%s\n' one two three)" # <<< is the here-string operator

echo "$out" # -> 'one/two/three'

Обратите внимание на необходимость двойного цитирования замены команды, чтобы защитить его вывод от Расширение раковины.


POSIX-совместимый решение (sh): использовать здесь с командование замены

#!/bin/sh

out=
while read -r var; do
  out="$out$var/"
done <<EOF # <<EOF ... EOF is the here-doc
$(printf '%s\n' one two three)
EOF

echo "$out" # -> 'one/two/three'

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top