
Eu tenho um arquivo de dados com alguns dados de amostra (Nome, Sobrenome, Renda, Taxa Rax). Preciso fazer alguns cálculos, mas na hora de exibir o resultado estou tendo problemas pois é um loop e não está exibindo na ordem. Sim, devo usar o bash.
Arquivo de exemplo:
------------------------------------
example-file.txt:
John,Rambo,99880,2%
Elon,Musk,144000,2%
------------------------------------
teste.bash:
#/bin/bash
FILE=example-file.txt
for file in $FILE;
do
# Get income
INCOME=$(cat $file | awk -F, '{print $3}');
FIRSTNAME=$(cat $file | awk -F, '{print $1}');
LASTNAME=$(cat $file | awk -F, '{print $2}');
# Get tax rate
RATE=$(cat $file | awk -F, '{print $4}' | sed 's/%//');
for foo in $INCOME
do
if [ "$foo" -ge 80000 ] && [ "$foo" -lt 150000 ];
then
for faa in $foo
do
NETINCOME=$(printf "%'.f\n" $(echo "(($INCOME * $RATE / 100))" | bc))
done
fi
echo "${FIRSTNAME} ${LASTNAME} ${NETINCOME}"
done
done
Saída:
(standard_in) 2: syntax error
(standard_in) 3: syntax error
John
Elon Rambo
Musk 288,000
(standard_in) 2: syntax error
(standard_in) 3: syntax error
John
Elon Rambo
Musk 288,000
Saída desejada:
John Rambo 1997
Elon Musk 2880
Como posso conseguir isso?
Responder1
Já que você está usando awk
para extrair campos de suas linhas, por que não deixa ele fazer todo o trabalho? Ele deve ter recursos aritméticos suficientes e converter seu script em um script AWK pode ser mais fácil do que corrigi-lo.
$ cat example-file.txt
John,Rambo,99880,2%
Elon,Musk,144000,2%
$ awk -v FS=, '{ print $1,$2,sprintf("%i", $3 * $4 / 100) }' example-file.txt
John Rambo 1997
Elon Musk 2880
Use o primeiro argumento fornecido sprintf
para ajustar o formato do número impresso.
Você também pode filtrar linhas. Por exemplo, supondo que você esteja interessado apenas em uma faixa de renda:
$ awk -v FS=, '
80000 < $3 && $3 < 150000 {
print $1,$2,sprintf("%i",$3 * $4 / 100)
}'
Ele aceita um número arbitrário de arquivos como argumentos (o limite superior depende da configuração do seu sistema; se você tiver muitos deles, use find ... -exec awk ...
):
awk -v FS=, '...' file1.txt file2.txt ...
Finalmente, você pode salvar o script como um arquivo, sem os '
s anexos, e invocá-lo como:
awk -v FS=, -f awk_script file1.txt
eliminando a necessidade de concatenações de strings inconvenientes caso você precisasse usar literais '
em algum lugar. Por exemplo, se você quisesse que a sprintf
string de formato incluísse o '
sinalizador, o script embutido se tornaria:
awk ... '... sprintf("%'"'"'.f", $3 * $4 / 100) ...' file
Responder2
Seu script tem vários problemas. Primeiro, você está tratando strings como arrays (não há razão ou sentido em fazer for foo in $bar
quando $bar
há apenas uma string). Segundo, você está lendo o arquivo várias vezes, quando tudo o que precisa fazer é lê-lo uma vez. Terceiro, você não está citando suas variáveis, o que significa que o script será interrompido se os nomes dos arquivos puderem conter espaços em branco. Aqui está uma maneira melhor:
#/bin/bash
## take a list of file names as an argument so you can
## work on multiple files.
for file in "$@";
do
## get the values
while read firstName lastName income rate; do
rate=${rate//%}
if [[ "$income" -ge 80000 && "$income" -lt 150000 ]]; then
netIncome=$(printf "%'.f\n" $(echo "($income * $rate) / 100" | bc))
## You didn't specify what should happen if the income isn't in the
## target range, so I am assuming you want it to be the $income unchanged.
else
netIncome=$income
fi
echo "$firstName $lastName $netIncome"
done < <(awk -F, '{print $1,$2,$3,$4}' "$file")
done
Você pode executar isso com os arquivos como argumentos:
$ foo.sh file
John Rambo 1,997
Elon Musk 2,880