paulo1205
(usa Ubuntu)
Enviado em 10/11/2014 - 05:40h
Como disse o colega, acima, seria melhor você explicar direito o que quer.
Sua dúvida parece relacionada a de outro tópico que você criou. Fico feliz que você tenha deixado de lado a ideia de colocar várias linhas numa variável só. No entanto, eu particularmente não gosto da sintaxe
«comando que produz várias linhas» | while read «variáveis»; do
# Extrai valores a partir de variáveis
«comandos»
done
porque a
pipeline provoca a criação de um outro processo. No caso acima, todo o bloco que vai do sinal
| até o
done fica nesse processo separado, e isso significa que qualquer variável que tenha sido definida ou alterada dentro do laço de repetição não terá tais definições ou alterações visíveis quando o laço acabar. Além disso, se algum comando dentro do laço de repetição tentar ler alguma coisa, essa leitura vai interferir com a fonte de dados do próprio
while read. Por essas razões, eu prefiro a seguinte sintaxe, usando descritores auxiliares.
# O descritor de arquivos número 3 é associado à saída do comando.
exec 3< <(«comando que produz várias linhas»)
# Redireciona somente a leitura das variáveis
while read «variáveis» <&3; do
# Aqui dentro, outros comandos que façam leitura mas que não
# estejam explicitamente com o redirecionamento “<&3” não vão
# interferir com a leitura das variáveis.
«comandos»
done
# Libera (fecha) o descritor auxiliar
exec 3<&-
No seu código, aquele
for me parece uma tentativa de procurar ocorrências múltiplas de um conjunto de strings dentro da mesma linha. Parece-me que isso deveria funcionar, mas tenho receio de que você esteja fazendo de uma forma pouco eficiente.
Um jeito rápido de fazer sem
while nem
for, mas usando comandos externos, seria com a opção “-o” do GNU
grep. Por exemplo:
«comando que gera múltiplas linhas» > arquivo_temporário
cont_FXS=`grep -o FXS arquivo_temporário | wc -l`
cont_FXO=`grep -o FXO arquivo_temporário | wc -l`
# etc...
rm -f arquivo_temporário
Outro modo, sem chamar comando externo, seria com outro tipo substituição do Bash, que remove prefixos.
while read linha <&3; do
a="$linha"
while : ; do
b="${a#*FXS}"
[[ "$b" = "$a" ]] && break
a="$b"
((cont_FXS++))
done
a="$linha"
while : ; do
b="${a#*FXO}"
[[ "$b" = "$a" ]] && break
a="$b"
((cont_FXO++))
done
# etc...
done
Para você saber, o Bash é ruim de desempenho com manipulação de strings e péssimo com aritmética. Mesmo normalmente preferindo evitar comandos externos, eu recomendo a sugestão com “grep -o” e “wc -l”. Sò para lhe dar uma ideia, eis uma comparação de desempenho entre comandos análogos ao que você fez e às duas sugestões que eu dei, contando ocorrência do string "man" na lista de arquivos da minha máquina (que tem um total de 345208 arquivos, ou linhas de entrada).
$ # Análogo ao que você fez: tempo de referência.
$ time locate "" | bash -c 'cont=0; while read linha; do for ((i=0; i<${#linha}-2; i++)); do if [[ ${linha:$i:3} = man ]]; then ((cont++)); fi; done; done; echo $cont'
36494
real 4m12.240s
user 3m58.714s
sys 0m13.982s
$ # Com grep -o e wc -l: 1510 vezes mais rápido!!!
$ time locate "" | grep -o man | wc -l
36494
real 0m0.167s
user 0m0.224s
sys 0m0.062s
$ # Extração de prefixos: 7,69 vezes mais rápido.
$ time locate "" | bash -c 'cont=0; while read linha; do a="$linha"; while :; do b=${a#*man}; [[ "$b" = "$a" ]] && break; a="$b"; ((cont++)); done; done; echo $cont'
36494
real 0m32.803s
user 0m17.763s
sys 0m15.319s
Com Perl, fica um pouco mais interessante:
$ time locate "" | perl -ne '
BEGIN {
@palavras=qw(share man obj kernel src module etc usr root);
$padrao=join("|", @palavras);
foreach $str (@palavras){
$cont{$str}=0;
}
}
while(s/.*?($padrao)//){
$cont{$1}++;
}
END {
foreach $str (@palavras){
print "Ocorrências de \"$str\": $cont{$str}\n";
}
}
'
Ocorrências de "share": 158878
Ocorrências de "man": 36494
Ocorrências de "obj": 874
Ocorrências de "kernel": 10999
Ocorrências de "src": 125461
Ocorrências de "module": 12269
Ocorrências de "etc": 3673
Ocorrências de "usr": 315613
Ocorrências de "root": 318
real 0m1.681s
user 0m1.833s
sys 0m0.156s