причудливое правило определения границ Баша ускользает от меня - PullRequest
4 голосов
/ 18 апреля 2011

Рассмотрим:

t=0 ; for i in 1 2 3 4 5 6 7 8 9 10 ; do t=$((t+i)) ; done ; echo $t

отпечатков 55.

Но:

totsize=0
find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du | \
while read size rest ; do
    totsize=$((totsize+size))
    echo "$totsize"
done
echo "Sum: $totsize kb"

Печатает «Sum: 0 kb», даже если промежуточный оператор печати печатает разумную сумму.

Я знаю, что сталкивался с этой проблемой раньше, но никогда не понимал ее. В чем разница?

Ответы [ 4 ]

8 голосов
/ 18 апреля 2011
totsize=0

while read size rest ; do
    totsize=$((totsize+size))
    echo "$totsize"
done < <(find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du)
echo "Sum: $totsize kb"

Предотвращение подоболочки, потому что подоболочка ограничит область действия totsize


Потеря еще нескольких слов:

do_something < <(subprocess)
  • будет выполнятьсяdo_something в основной оболочке (это перенаправление ввода с заменой процесса )

.

subprocess | do_something
  • будет выполнятьсяdo_something в отдельной (под) оболочке (это подпроцесс pipe)
6 голосов
/ 18 апреля 2011

это потому, что каналы создают подоболочку, поэтому totsize является «локальным» внутри этой подоболочки. Вы можете попробовать это вместо этого (bash)

totsize=0
while read size rest ; do
    totsize=$((totsize+size))
    echo "$totsize"
done < <(find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du)
echo "Sum: $totsize kb"

Или вместо использования bash позвоните awk

$> find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du | awk  '{s+=$1}END{print "total size: "s}'

Но вы уверены, что хотите использовать просто du без каких-либо опций, потому что размер не "точен" (лучше использовать du -b). Если у вас есть GNU find, вы можете использовать -printf

find /home/user -type f -mmin -4860 -a -mmin +3420 -printf "%s\n" | awk  '{s+=$1}END{print "total size: "s" bytes"}'
1 голос
/ 18 апреля 2011

Это распространенная проблема.Правосторонние элементы трубопровода проходят в подоболочке.В этом случае и xargs, и while будут выполняться в дочернем процессе оболочки (все они выполняются параллельно, поэтому они должны быть отдельными процессами).

Вам потребуется выполнить рефакторинг своего кода так,Вы не накапливаете значение в подоболочке.Другие показали bash рефакторинг.Вот метод, использующий awk:

find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du \
| awk '{sum += $1} END {print sum}'

Поместите это в функцию оболочки, если вы хотите записать сумму в переменную:

sum_usage() { ...above pipeline... ; }
totsize=$(sum_usage)

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

0 голосов
/ 18 апреля 2011

Вероятно, это происходит из-за некоторого создания подоболочки (см. man bash, найдите КОМАНДА ИСПОЛНЕНИЯ ИСПОЛНЕНИЯ ).

Но Вы можете обойтись без while:

find . -type f -mmin -4860 -a -mmin +3420 | xargs du | awk '{sum=sum+$1} END {print "Total: " sum " kb."}'

НТН

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...