Я новичок в написании сценариев на bash.
Попытка написать простой скрипт, который читает из стандартного ввода (файла) и записывает в стандартный вывод. Я хочу, чтобы сценарий подсчитывал количество параметров / арифметических расширений и замен команд.
Для стандартного ввода я пока делал следующее:
file=${1--}
while IFS= read -r line; do
printf '%s\n' "$line"
done < <(cat - "$file")
Просто используйте grep
с флагом -c
для подсчета, или еще лучше grep -o '<PATTERN>' | wc -l
(см. Примечание об этом позже в тексте). В зависимости от того, как вы хотите, чтобы ваш вывод выглядел, и в зависимости от того, чего вы хотите достичь, существует несколько способов его настройки.
Конечно, существуют разные типы расширения параметров, поэтому нам нужно написать для них разные шаблоны. Здесь я только что написал замену шаблонов, удаление префиксов и арифметическое расширение, но вы можете адаптировать их к любому другому.
На самом базовом уровне мы можем вызывать отдельные команды так:
$ cat ./myscript.sh
#!/bin/bash
var="string"
echo "${var/string/thread}"
echo $((2+2))
echo $((3*2))
echo ${var#*i}
echo ${var##*i}
echo ${var%i*}
echo ${var%%i*}
$ grep -c '${.*}' myscript.sh
1
$ grep -c '$((.*))' myscript.sh
2
И это только основная идея. Вы можете поместить несколько grep
паттернов в скрипт с хорошим начальным сообщением. Примерно так:
#!/usr/bin/env bash
printf "\nNumber of arithmetic expansions:"
grep -c '$((.*))' "$1"
printf "\nNumber of substitutions:"
grep -c '${.*\/.*\/.*}' "$1"
printf "\nNumber of prefix removals:"
grep -c '${.*[#]\{1,2\}.*}' "$1"
И затем вызвать этот скрипт как:
./count_bash_stuff.sh myscript.sh
Либо просто поместите все шаблоны в файл и вызовите grep
с помощью -f
. Конечно, он выведет только общее количество всех совпадений , а не отдельных:
$ cat ./patterns
$((.*))
${.*\/.*\/.*}
${.*[#]\{1,2\}.*}
$ grep -c -f ./patterns ./myscript.sh
5
Примечание: как указал Steeldriver в комментарии под этим ответом, grep
инструмент сопоставления строк, который означает, что если в строке более одного паттерна, он добавит 1 для подсчета только для этой линии. Лучше было бы использовать флаг -o
с grep
и направить вывод в wc -l
. Таким образом, вместо этого мы могли бы сделать что-то подобное для арифметического расширения:
grep -o '$((.*))' | wc -l
Да, это возможно. Это сложно, но возможно с case
утверждениями. Здесь я просто собираюсь написать case
оператор для арифметического расширения и удаления самого длинного префикса. Остальное зависит от читателя, чтобы адаптироваться и расширяться.
/bin/sh
- в Ubuntu это POSIX-совместимая оболочка /bin/dash
, а не bash
.
#!/usr/bin/env sh
while IFS="" read -r line || [ -n "$line" ];
do
case "$line" in
# arithmetic expansion, appearing anywhere in line
*'$(('*'))'*) arithmetic_count=$(($arithmetic_count+1));;
*'${'*##*'}'* ) prefix_count=$((prefix_count+1));;
esac
done < "$1"
echo "$arithmetic_count"
echo "$prefix_count"
И это выглядит так:
$ ./count_shell_stuff.sh myscript.sh
2
1
Почему это не так практично, спросите вы? Потому что написание шаблонов в case
может быть несколько сложным и немного сложнее, чем, скажем, в grep
или Perl
. Кроме того, вам придется написать case
для каждой синтаксической структуры оболочки. С помощью grep
или perl
вы можете объединить несколько шаблонов в один. Но опять же, это только мое личное мнение. Сопоставление с строковым образцом все еще очень возможно, как я показал в этом небольшом доказательстве концепции, но я не рекомендовал бы это как способ.