Переменные получают сброс после цикла чтения, который считывается из трубопровода
-
26-10-2019 - |
Вопрос
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
, в этом случае - в самом начале линии, и что ни один символ не должен следовать за ней.