Leitura de caractere por caractere com bash read

8

Estou tentando usar o bash para ler um caractere de arquivo por caractere.

Depois de muita tentativa e erro, descobri que isso funciona:

exec 4<file.txt 
declare -i n
while read -r ch <&4; 
     n=0
     while [ ! $n -eq ${#ch} ]
           do  echo -n "${ch:$n:1}"
               (( n++ ))
          done
     echo "" 
     done

Ou seja, eu posso lê-lo linha por linha e, em seguida, percorrer cada linha char por char.

Antes de fazer isso, eu tentei: %código% mas iria todos os espaços em branco no arquivo .

Você poderia por favor explicar por quê? Existe uma maneira de fazer a segunda estratégia (ou seja, ler char por char com o ler de bash) funcionar?

    
por PSkocik 01.10.2012 / 14:29

2 respostas

12

Você precisa remover os caracteres de espaço em branco do parâmetro $IFS para read para parar de pular os pontos iniciais e posteriores (com -n1 , o caractere de espaço em branco se algum deles estiver à esquerda e à direita):

while IFS= read -rn1 a; do printf %s "$a"; done

Mas, mesmo assim, o read do bash irá ignorar caracteres de nova linha, com os quais você pode contornar:

while IFS= read -rn1 a; do printf %s "${a:-$'\n'}"; done

Embora você possa usar IFS= read -d '' -rn1 ou melhor IFS= read -N1 (adicionado em 4.1, copiado de ksh93 (adicionado em o )), que é o comando para ler um caractere.

Observe que o read do bash não pode lidar com caracteres NUL. E o ksh93 tem os mesmos problemas que o bash.

Com zsh:

while read -ku0 a; do print -rn -- "$a"; done

(zsh pode lidar com caracteres NUL).

Note que os read -k/n/N lêem um número de caracteres , não bytes . Portanto, para caracteres multibyte, eles podem ter que ler vários bytes até que um caractere completo seja lido. Se a entrada contiver caracteres inválidos, você pode acabar com uma variável que contém uma seqüência de bytes que não formam caracteres válidos e que o shell pode acabar contando como vários caracteres . Por exemplo, em uma localidade UTF-8:

$ printf '50000ABC' | bash -c '
    IFS= read  -rN1 a; echo "${#a}"'
6

Esse 5 introduziria um caractere UTF-8 de 6 bytes. No entanto, o sexto ( A ) acima é inválido para um caractere UTF-8. Você ainda acaba com 50000A em $a , que bash conta como 6 caracteres embora os primeiros 5 não sejam realmente caracteres, apenas 5 bytes não fazem parte de nenhum caractere. / p>     

por 01.10.2012 / 15:01
2

Este é um exemplo simples usando cut , for loop & wc :

bytes=$(wc -c < /etc/passwd)
file=$(</etc/passwd)

for ((i=0; i<bytes; i++)); do
    echo $file | cut -c $i
done

KISS não é?

    
por 01.10.2012 / 14:47

Tags