Script do shell: alteração no fluxo de entrada que não funciona como desejado

2

Estou tentando criar um script de shell que possa contar palavras e linhas em um arquivo. (sim, eu sei que você pode fazer isso usando wc, mas apenas por uma questão de aprender loops)

Código

echo "enter filename"
read f
if [ -s $f ]
then
    terminal='tty'

    exec < $f

    nol=0
    now=0

    while read line
    do
        nol='expr $nol + 1'
        set $line
        now='expr $now + $#'
    done

    echo "No.of lines in the file =$nol"
    echo "No.of words in the file =$now"

    exec < $terminal
fi

resultado esperado:

No.of lines in the file =3
No.of words in the file =166

Saída real:

$ sh wc.sh 
enter filename
spock.txt
CLUTTER_IM_MODULE='xim'
COLORTERM='gnome-terminal'
COMPIZ_BIN_PATH='/usr/bin/'
COMPIZ_CONFIG_PROFILE='ubuntu'
DBUS_SESSION_BUS_ADDRESS='unix:abstract=/tmp/dbus-PfcwnNxO9c'
DEFAULTS_PATH='/usr/share/gconf/ubuntu.default.path'
DESKTOP_SESSION='ubuntu'
DISPLAY=':0'
...
XDG_MENU_PREFIX='gnome-'
XDG_RUNTIME_DIR='/run/user/1000'
XDG_SEAT='seat0'
XDG_SEAT_PATH='/org/freedesktop/DisplayManager/Seat0'
XDG_SESSION_ID='c1'
XDG_SESSION_PATH='/org/freedesktop/DisplayManager/Session0'
XDG_VTNR='7'
XMODIFIERS='@im=ibus'
_='/bin/sh'
f='spock.txt'
line=''
nol='2'
now='66'
terminal='/dev/pts/0'
No.of lines in the file =3
No.of words in the file =166

Alguém poderia esclarecer o que estou fazendo errado? obrigado.

    
por TheWiz 12.03.2015 / 06:05

1 resposta

0

Provavelmente na última linha, ou em algum lugar no meio, há uma linha vazia. Quando você executa set sem argumentos, ele imprime todas as variáveis definidas no shell atual. Da especificação POSIX :

  

Se nenhuma opção ou argumento for especificado, o conjunto deve escrever os nomes e valores de todas as variáveis do shell na seqüência de agrupamento da localidade atual.

Isso vai atrapalhar seu cálculo, já que isso não define os argumentos para a lista vazia - você estará adicionando a contagem de palavras da linha anterior novamente.

Você está obtendo essa saída porque $line está vazio, então o que você deve fazer é verificar se está, de fato, vazio:

while read line
do
    nol='expr $nol + 1'
    if [ -n "$line" ]
    then
        set $line
        now='expr $now + $#'
    fi
done

Além disso, em vez de exec <$f , sugiro usar:

while read ..
do 
    ...
done < "$f"

O que você está fazendo agora é:

terminal='tty'
exec < $f    
...
exec < $terminal

Você está redirecionando o script inteiro, enquanto você só precisa do redirecionamento para o loop. Você pode pular a chamada para tty e os dois exec s:

if [ -s $f ]
then
    nol=0
    now=0

    while read line
    do
        ...
    done < "$f"
    ...
fi

E use a expansão aritmética em vez da substituição de comandos com expr :

nol=$((nol + 1))
now=$((now + $#))

E sempre, sempre , cite suas variáveis, a menos que você queira explicitamente dividir:

  • [ -s "$f" ]
  • exec < "$f"

Considere o que acontece quando f contém espaços:

f="there are spaces in this filename"
[ -s $f ]
exec < $f

Você receberá erros, porque, uma vez que $f seja substituído por seu conteúdo, cada palavra será vista separadamente:

bash: [: too many arguments
bash: $f: ambiguous redirect
    
por muru 12.03.2015 / 06:31