Сумма в bash снаружи во время чтения строки

Это должно быть там. Диски в Linux (и Windows, но неважно ), смонтированыв определенном месте. Эта кнопка позволит вам размонтировать этот диск.

В этом нет ничего плохого, и это может быть полезно. Если он вам не нужен, просто игнорируйте его, но нет причин его удалять.

3
04.10.2019, 18:19
4 ответа

bash выполняет каждую команду конвейера в отдельной подоболочке, если вы не включите параметр оболочки lastpipe

# bash requires job control to be disabled for lastpipe setting
set +m
shopt -s lastpipe

declare -i sum=0
find. -name '*.js' -print0 | while IFS= read -d '' -r name; do
    (( sum += $(wc -l < "$name") ))   # redirect the file into wc for easier output
done
echo $sum

Подстановки процессов удобны для решения этой проблемы с подоболочкой:

declare -i sum=0
while IFS= read -d '' -r name; do
    (( sum += $(wc -l < "$name") ))   # redirect the file into wc for easier output
done < <(
    find. -name '*.js' -print0
)
echo $sum

Однако это затрудняет чтение программы.

5
27.01.2020, 21:07

Вот еще один способ сделать это с помощью bash:

sum=0 && while IFS= read -r -d '' f; do let sum+=$(sed -n "\$=" "${f}"); done < <(find. -name '*.js' -print0 2>/dev/random) && echo "$sum"

find... -print0 -- prints with null terminated 
            IFS= -- deset IFS 
   read -r -d '' -- -r do not allow backslash escapes, -d '' set delimiter to NULL
   let sum+=(..) -- add up sum of sed $= "$f", $: last line, =: line #
0
27.01.2020, 21:07

Вы хотите, чтобы awkвыполнил сложение и показал результат?

awk '{sum +=$1} END {print sum}'должно помочь.

В моей библиотеке для bash -скрипты, которые я делаю:

$ find. -type f -name '*.bash' \
| while read -r f ; do wc -l "$f" ; done \
| awk '{sum +=$1} END {print sum}'

и получить результат522

5
27.01.2020, 21:07

Аннотация

Для подсчета строк в каталоге:

shopt -s globstar;                      # valid for bash
set --./**/*".js"; cat "$@" | wc -l    # for files under `./` directory

Для суммирования вне цикла чтения while

shopt -s globstar;                                 # valid for bash
set --./**/*".js"                                 # for files under `./` directory
wc -l "$@" | awk '{sum+=$1} END {print sum-=$1}'   # calculate the sum in awk

Но зачем повторно -вычислять сумму, если wc -lвыводит итог в последней строке?:

wc -l "$@" | tail -n 1


Деталь

Есть несколько элементов, которые можно улучшить:

  • Часть | awk '{print $1;}'для выбора только первого поля не требуется, если вы выполняете wc -l <"$f"вместо wc -l $f. Простое перенаправление(<)заставляет wc получить файл на свой стандартный ввод, и у него не будет имени файла для печати. Это уменьшит сценарий до:

    найти. | grep ".js" | при чтении -r f; do wc -l <"$f"; сделано

  • Нет необходимости в вызове grep, если find делает выбор:

    найти. -имя '*.js' | при чтении -r f; do wc -l <"$f"; сделано

  • При чтении удаляются начальные и конечные пробелы из имен файлов.

  • И на самом деле, find может выполнять команду для каждого файла (неявный цикл):

    найти. -name ' *.js' -exec sh -c 'wc -l <"$1"' foo '{}' \;

  • И даже можно сделать один глобальный вызов wcвместо одного для каждого файла.

    найти.-имя ' *.js' -exec sh -c 'cat "$@" | wc -l' foo '{}' +

Но необходимость повторно -вызывать оболочку для обработки каждого имени файла без каких-либо проблем с пробелами, табуляциями, символами новой строки или подстановочными символами (*,?,[ )указывает на то, что мы можем решить эту проблему непосредственно в оболочке. если нам не нужно какое-то специальное разрешение ссылок.

set -- *.js; cat "$@" | wc -l # for the present directory

Или

shopt -s globstar;                      # valid for bash
set --./**/*".js"; cat "$@" | wc -l    # for files under `./` directory

Сумма вне цикла чтения while

Вопрос в заголовке касается этой части вашей трубки:

while read -r f; do wc -l $f …

Предполагая, что список файлов находится в списке аргументов($@)(или может быть внутри некоторого массива ), как указано выше, будет напечатан список файлов с количеством строк в качестве первого поля:

$ printf '%s\n' "$@" | while read -r f; do wc -l "$f"; done
12 filea.js
21 fileb.js    

В этот момент вы можете просто добавить новый канал с помощью awk, чтобы выбрать первое поле:

$ printf '%s\n' "$@" | while read -r f; do wc -l "$f"; done | awk '{print $1}'
12
21

Но с тем же успехом вы могли бы напечатать все в одной строке с добавлением +:

$ printf '%s\n' "$@" | 
> while read -r f; do wc -l "$f"; done | 
> awk '{printf( "%s+",$1)}'
12+21+

И, добавляя замыкающий 0, заставьте bc суммировать все это:

$ printf '%s\n' "$@" | 
> while read -r f; do wc -l "$f"; done |
> awk '{printf("%s+",$1)}END{print 0}' |
> bc
33

Но, как уже было сказано, вы можете избежать вывода имени файла с помощью wc -l <"$f"`` and you can convert the newlines to+ , then add a0 `и заставить bc выполнять вычисления:

$ printf '%s\n' "$@" |
  while read -r f; do wc -l <"$f"; done |
  { tr '\n' '+'; echo 0; } |
  bc

33

или заставить awk вычислить сумму:

$ printf '%s\n' "$@" | 
  while read -r f; do wc -l <"$f"; done | 
  awk '{sum+=$1} END {print sum}'

33
1
27.01.2020, 21:07

Теги

Похожие вопросы