Eu tenho tentado usar o bash para ler um arquivo caractere por caractere.
Após muitas tentativas e erros, 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 ler linha por linha e, em seguida, percorrer cada linha char por char.
Antes de fazer isso, eu tentei:
exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done
mas pularia todos os espaços em branco no arquivo .
Poderia explicar por que? Existe uma maneira de fazer a segunda estratégia (isto é, ler char por char com a leitura do bash) funcionar?
IFS
como nada para que os espaços em branco sobrevivam à divisão de palavras.Respostas:
Você precisa remover os caracteres de espaço em branco do
$IFS
parâmetro pararead
parar de ignorar caracteres iniciais e finais (com-n1
, o caractere de espaço em branco, se houver algum, seria inicial e final, portanto, ignorado):Mas, mesmo assim, o bash
read
ignora os caracteres de nova linha, com os quais você pode contornar:Embora você possa usar em
IFS= read -d '' -rn1
vez disso ou até melhorIFS= read -N1
(adicionado em 4.1, copiado deksh93
(adicionadoo
))) que é o comando para ler um caractere.Observe que o bash
read
não pode lidar com caracteres NUL. E o ksh93 tem os mesmos problemas que o bash.Com zsh:
(zsh pode lidar com caracteres NUL).
Observe que aqueles
read -k/n/N
leem vários caracteres , não bytes . Portanto, para caracteres multibyte, eles podem precisar ler vários bytes até que um caractere completo seja lido. Se a entrada contiver caracteres inválidos, você poderá acabar com uma variável que contém uma sequência de bytes que não forma caracteres válidos e que o shell pode acabar contando como vários caracteres . Por exemplo, em um código de idioma UTF-8:Isso
\375
introduziria um caractere UTF-8 de 6 bytes. No entanto, o sexto (A
) acima é inválido para um caractere UTF-8. Você ainda termina com\375\200\200\200\200A
in$a
, quebash
conta como 6 caracteres, embora os 5 primeiros não sejam realmente caracteres, apenas 5 bytes não fazem parte de nenhum caractere.fonte
read -rN1
resolve o problema da nova linha e, assim, elimina a necessidade de fornecer uma nova linha como padrão durante a impressão$a
.read -n1
(caractere por caractere) leva 4 minutos e 51 segundos e aquece o laptop a 90 graus. O usoread -r
(linha por linha) leva 1,3 segundos e o laptop fica a 54 graus com o ventilador duplo silencioso.Este é um exemplo simples usando
cut
, umfor
loop &wc
:BEIJO, não é?
fonte
bash
solução purafile="$(</etc/passwd)"; bytes="${#file}"; for ((i=0;i<bytes;i++)); do echo "${file:i:1}"; done
:?bash
"É muito grande e muito lento". de acordo com a seção BUGS da sua página de manual. Mas, mesmo assim, ainda é mais rápido dividir uma sequência na memória do que ler um arquivo repetidamente para cada caractere. Pelo menos na minha máquina: pastebin.com/zH5trQQs