Почему моя переменная, похоже, не увеличивается в моем цикле bash while?

#bash #shell #variables #scripting

#bash #оболочка #переменные #сценарии

Вопрос:

Я довольно новичок в написании сценариев bash. Кажется, я не могу получить правильное значение моих переменных подсчета для отображения в конце while цикла в моем скрипте bash.

Справочная информация: У меня есть довольно простая задача: я хотел бы передать текстовый файл, содержащий список путей к файлам, скрипту bash, заставить его проверить существование этих файлов и подсчитать количество существующих / отсутствующих файлов. Я заставил большую часть скрипта работать, за исключением части подсчета.

 N=0
correct=0
incorrect=0
cat $1 | while read filename ; do
    N=$((N 1))
    echo "$N"

    if ! [ -f $filename ]; then

        incorrect=$((incorrect 1))
    else
        correct=$((correct 1))

    fi

done

echo "# of Correct Paths: $correct"
echo "# of Incorrect Paths: $incorrect"
echo "Total # of Files: $N"
  

Если у меня есть список из 5 файлов, 4 из которых существуют, я ожидаю получить следующий вывод (обратите внимание на echo команду в while цикле):

 1
2
3
4
5
# of Correct Paths: 4
# of Incorrect Paths: 1
Total # of Files: 5
  

Вместо этого я получаю:

 1
2
3
4
5
# of Correct Paths: 0
# of Incorrect Paths: 0 
Total # of Files: 0
  

Что случилось со значениями этих переменных? У Google было много предложений сомнительного качества, и я думаю, что мог бы заставить это работать с помощью немного большего поиска, но краткое объяснение того, что я делаю неправильно, было бы очень полезно.

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

1. Вы можете использовать ((N )) и ((incorrect )) (или даже ((N =1)) ) для более сжатого синтаксиса.

Ответ №1:

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

 while read filename ; do
    N=$((N 1))
    ....
done < file
  

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

1. Спасибо, это решило мою проблему. Это лучший способ передать файл в цикл while? (т. Е. готово < файл). Это работает, но мне кажется немного неловким.

2. @spadina. ну, < , << > и >> являются базовыми операторами в bash. Вы увидите это много раз при написании сценариев bash. Привыкание к этому облегчит вам эту «неловкость».

3. Если вам нужно сохранить подоболочку для вашего цикла, вы также можете записать обновленную переменную во временный файл. Например: N=`cat /tmp/n`; ((N )); echo $N > /tmp/n . Когда цикл будет завершен, снова прочитайте его из файла: N=`cat /tmp/n` .

Ответ №2:

В качестве альтернативы, если вы по какой-то причине хотите сохранить cat , вы можете исправить свой скрипт, просто добавив эту строку перед cat инструкцией:

 shopt -s lastpipe 
  

Ответ №3:

В более общем плане, иногда вы хотите передать вывод команды по каналу. Вот пример, в котором используется подстановка процесса для удаления файлов JavaScript, которые Git собирается зафиксировать, и подсчитывается количество файлов, которые завершились ошибкой:

 # $@ glob
git-staged-files() {
  git diff --cached -C -C -z --name-only --relative --diff-filter=ACMRTUXB "$@"
}

# $@ name
map() { IFS= read -rd $'' "$@"; }

declare -i errs=0
while map file; do
  echo "Checking $file..."
  git show ":$file"|
  eslint --stdin --stdin-filename "$file" || ((  errs))
done < <(git-staged-files *.js)

((errs)) amp;amp; echo -en "e[31m$errs files with errors.e[00m " >amp;2 || :