bash :функции, содержащие часть кода AWK

Аннотация

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

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
0
28.10.2020, 16:24
1 ответ

Просто измените ${file} на "$1" внутри вашей функции, и она будет делать то, что вы хотите.

Также рассмотрите возможность изменения этого:

bar_xvg_proc () {
##AWK procession of XVG file: only for bar plot;
sed -i '' -e '/^[#@]/d' "$1"
# check XMAX and YMAX for each XVG
awk '
  NR==1{
    max1=$1
    max2=$2
  }
  $1>max1{max1=$1}
  $2>max2{max2=$2}
  END{printf "WORLD XMAX %s\nWORLD YMAX %s\n",max1+0.5,max2+0.5'} "$1" >> "${tmp}"/grace2.bfile

}

к этому:

bar_xvg_proc () {
    ##AWK procession of XVG file: only for bar plot;
    # check XMAX and YMAX for each XVG
    awk '
      /^[#@]/ { next }
      (++nr)==1{
        max1=$1
        max2=$2
      }
      $1>max1{max1=$1}
      $2>max2{max2=$2}
      END{printf "WORLD XMAX %s\nWORLD YMAX %s\n",max1+0.5,max2+0.5'} "${@:--}" >> "${tmp}"/grace2.bfile
}

Вам никогда не понадобится sed, когда вы используете awk, и использование "${@:--}"таким образом позволяет вам иметь функцию, которая будет работать независимо от того, передаете ли вы ей несколько имен файлов или передаете ей поток, поскольку она говорит awk использовать stdin, если файл отсутствует.

Не знаю, стоит ли вам действительно использовать >>вместо >в конце этого, и вы можете захотеть выполнить перенаправление вывода вне функции.

2
18.03.2021, 22:54

Теги

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