Подтвердить что ты не робот

Дилемма подголовника в замкнутом цикле в Bash

Я хочу вычислить все файлы * bin внутри заданного каталога. Сначала я работал с for-loop:

var=0
for i in *ls *bin
do
   perform computations on $i ....
   var+=1
done
echo $var

Однако в некоторых каталогах слишком много файлов, приводящих к ошибке: Argument list too long

Поэтому я пытался с помощью while-loop:

var=0
ls *.bin | while read i;
do
  perform computations on $i
  var+=1
done
echo $var

Теперь проблема заключается в использовании подстроек труб. Таким образом, echo $var возвращает 0.
Как я могу справиться с этой проблемой?
Оригинальный код:

#!/bin/bash

function entropyImpl {
    if [[ -n "$1" ]]
    then
        if [[ -e "$1" ]]
        then
            echo "scale = 4; $(gzip -c ${1} | wc -c) / $(cat ${1} | wc -c)" | bc
        else
            echo "file ($1) not found"
        fi
    else
        datafile="$(mktemp entropy.XXXXX)"
        cat - > "$datafile"
        entropy "$datafile"
        rm "$datafile"
    fi

    return 1
}
declare acc_entropy=0
declare count=0

ls *.bin | while read i ;
do  
    echo "Computing $i"  | tee -a entropy.txt
    curr_entropy=`entropyImpl $i`
    curr_entropy=`echo $curr_entropy | bc`  
    echo -e "\tEntropy: $curr_entropy"  | tee -a entropy.txt
    acc_entropy=`echo $acc_entropy + $curr_entropy | bc`
    let count+=1
done

echo "Out of function: $count | $acc_entropy"
acc_entropy=`echo "scale=4; $acc_entropy / $count" | bc`

echo -e "===================================================\n" | tee -a entropy.txt
echo -e "Accumulated Entropy:\t$acc_entropy ($count files processed)\n" | tee -a entropy.txt
4b9b3361

Ответ 1

Проблема в том, что цикл while выполняется в подоболочке. После завершения цикла while копия подголовника var отбрасывается, а исходный var родителя (значение которого не изменяется) эхом.

Один из способов исправить это можно с помощью "Замена процесса" , как показано ниже:

var=0
while read i;
do
  # perform computations on $i
  ((var++))
done < <(find . -type f -name "*.bin" -maxdepth 1)

Посмотрите BashFAQ/024 для других обходных путей.

Заметьте, что я также заменил ls на find, потому что это не хорошая практика parse ls.

Ответ 2

Решение, совместимое с POSIX, должно состоять в использовании файла pipe (p file). Это решение очень красивое, портативное и POSIX, но что-то пишет на жестком диске.

mkfifo mypipe
find . -type f -name "*.bin" -maxdepth 1 > mypipe &
while read line
do
    # action
done < mypipe
rm mypipe

Ваша труба - это файл на вашем жестком диске. Если вы хотите избежать ненужных файлов, не забудьте удалить его.

Ответ 3

Таким образом, исследуя общую проблему, передавая переменные из цикла подзаголовка в родительский цикл. Единственное решение, которое я нашел, здесь отсутствовало, это использовать здесь-строку. Поскольку это был bash-ish, и я предпочел решение POSIX, я обнаружил, что строка здесь - это просто ярлык для документа здесь. Имея эти знания под рукой, я придумал следующее, избегая подоболочки; таким образом позволяя переменным быть установленными в цикле.

#!/bin/sh

set -eu

passwd="username,password,uid,gid
root,admin,0,0
john,appleseed,1,1
jane,doe,2,2"

main()
{
    while IFS="," read -r _user _pass _uid _gid; do
        if [ "${_user}" = "${1:-}" ]; then
            password="${_pass}"
        fi
    done <<-EOT
        ${passwd}
    EOT

    if [ -z "${password:-}" ]; then
        echo "No password found."
        exit 1
    fi

    echo "The password is '${password}'."
}

main "${@}"

exit 0

Одно важное замечание для всех пастеров копирования заключается в том, что документ здесь настроен с использованием дефиса, указывающего, что вкладки следует игнорировать. Это необходимо, чтобы макет был приятным. Важно отметить, что stackoverflow не отображает вкладки в "коде" и заменяет их пробелами. Grmbl. Так что, не портите мой код, просто потому, что вы, ребята, предпочитаете пробелы перед вкладками, это не имеет значения в этом случае!

Это, вероятно, ломается на другом редакторе (настройках), а что нет. Таким образом, альтернативой было бы иметь это как:

    done <<-EOT
${passwd}
EOT

Ответ 4

Это также можно сделать с помощью цикла for:

var=0;
for file in `find . -type f -name "*.bin" -maxdepth 1`; do 
    # perform computations on "$i"
    ((var++))
done 
echo $var