A maneira prática
Use apenas grep
com -c
flag para contar ou, melhor ainda, grep -o '<PATTERN>' | wc -l
(veja a nota posteriormente no texto). Dependendo de como você deseja que sua saída se pareça e dependendo do que você deseja alcançar, há várias maneiras de configurá-la.
É claro que existem diferentes tipos de expansão de parâmetros, por isso precisamos escrever padrões diferentes para eles. Aqui eu apenas escrevi substituição de padrões, remoção de prefixo e expansão aritmética, mas você pode adaptá-los a qualquer outro.
No nível básico, podemos chamar comandos individuais da seguinte forma:
$ 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
E essa é apenas a ideia básica. Você pode colocar vários padrões grep
em um script com uma boa mensagem inicial. Algo como:
#!/usr/bin/env bash
printf "\nNumber of arithmetic expansions:"
grep -c '$((.*))' ""
printf "\nNumber of substitutions:"
grep -c '${.*\/.*\/.*}' ""
printf "\nNumber of prefix removals:"
grep -c '${.*[#]\{1,2\}.*}' ""
E então chame esse script como:
./count_bash_stuff.sh myscript.sh
Como alternativa, basta colocar todos os padrões no arquivo e chamar grep
com -f
. Obviamente, ele produzirá apenas a contagem total de todas as correspondências não individuais:
$ cat ./patterns
$((.*))
${.*\/.*\/.*}
${.*[#]\{1,2\}.*}
$ grep -c -f ./patterns ./myscript.sh
5
Observação: como a steeldriver apontou no comentário abaixo desta resposta, grep
é uma ferramenta de correspondência de linha, o que significa que, se houver mais de um padrão em uma linha, ele adicionará 1 para contar apenas para essa linha. A melhor abordagem seria usar -o
flag com grep
e canalizar a saída para wc -l
. Assim, em vez disso, poderíamos fazer algo assim para a expansão aritmética:
grep -o '$((.*))' | wc -l
O modo de shell POSIX (não tão prático)
Sim, isso é possível. É difícil, mas é possível com instruções case
. Aqui só vou escrever a instrução case
para expansão aritmética e remoção de prefixo mais longa. O resto depende do leitor para se adaptar e ampliar.
Novamente, lembre-se de que esta é uma prova de conceito. Observe que isso usa /bin/sh
- no Ubuntu isso é compatível com POSIX /bin/dash
shell, não 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 < ""
echo "$arithmetic_count"
echo "$prefix_count"
E é assim:
$ ./count_shell_stuff.sh myscript.sh
2
1
Por que isso não é tão prático que você pode perguntar? Porque escrever padrões em case
pode ser um pouco desafiador e um pouco mais difícil do que em grep
ou Perl
. Além disso, você terá que escrever case
para cada estrutura de sintaxe do shell. Com grep
ou perl
você pode combinar vários padrões em um. Mas, novamente, esta é apenas minha opinião pessoal. A correspondência de padrões de sequência ainda é muito possível, como demonstrei nesta pequena prova de conceito, mas não recomendaria isso como um caminho a ser seguido.