J'ai essayé d'utiliser bash pour lire un fichier caractère par caractère.
Après de nombreux essais et erreurs, j'ai découvert que cela fonctionne:
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
C'est-à-dire que je peux le lire ligne par ligne, puis parcourir chaque ligne char par char.
Avant de faire cela, j'avais essayé:
exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done
mais cela ignorerait tous les espaces dans le fichier .
Pourriez-vous expliquer pourquoi? Existe-t-il un moyen de faire fonctionner la deuxième stratégie (c'est-à-dire lire caractère par caractère avec la lecture de bash)?
IFS
à rien pour que les espaces blancs survivent à la séparation des mots.Réponses:
Vous devez supprimer les caractères d'espacement du
$IFS
paramètre pourread
arrêter de sauter les caractères de début et de fin (avec-n1
, le caractère d'espacement s'il y en a serait à la fois de début et de fin, donc ignoré):Mais même dans ce cas, bash
read
ignorera les caractères de nouvelle ligne, avec lesquels vous pouvez contourner:Bien que vous puissiez utiliser à la
IFS= read -d '' -rn1
place ou même mieuxIFS= read -N1
(ajouté en 4.1, copié depuisksh93
(ajouté eno
)) qui est la commande pour lire un caractère.Notez que bash ne
read
peut pas gérer les caractères NUL. Et ksh93 a les mêmes problèmes que bash.Avec zsh:
(zsh peut gérer les caractères NUL).
Notez que ceux-ci
read -k/n/N
lisent un certain nombre de caractères , pas d' octets . Ainsi, pour les caractères multi-octets, ils peuvent avoir à lire plusieurs octets jusqu'à ce qu'un caractère complet soit lu. Si l'entrée contient des caractères non valides, vous pouvez vous retrouver avec une variable qui contient une séquence d'octets qui ne forme pas de caractères valides et que le shell peut finir par compter comme plusieurs caractères . Par exemple, dans un environnement local UTF-8:Cela
\375
introduirait un caractère UTF-8 de 6 octets. Cependant, le 6ème (A
) ci-dessus n'est pas valide pour un caractère UTF-8. Vous vous retrouvez toujours avec\375\200\200\200\200A
in$a
, quibash
compte pour 6 caractères bien que les 5 premiers ne soient pas vraiment des caractères, seulement 5 octets ne faisant partie d'aucun caractère.la source
read -rN1
résout le problème de la nouvelle ligne et élimine ainsi la nécessité de fournir une nouvelle ligne par défaut lors de l'impression$a
.read -n1
(caractère par caractère) prend 4 min 51 secondes et chauffe l'ordinateur portable à 90 degrés. L'utilisationread -r
(ligne par ligne) prend 1,3 seconde et l'ordinateur portable reste à 54 degrés avec deux ventilateurs silencieux.Ceci est un exemple simple utilisant
cut
unefor
boucle &wc
:KISS n'est-ce pas?
la source
bash
solution:file="$(</etc/passwd)"; bytes="${#file}"; for ((i=0;i<bytes;i++)); do echo "${file:i:1}"; done
?bash
"C'est trop grand et trop lent." selon la section BUGS de sa page de manuel. Mais même ainsi, il est toujours plus rapide de découper une chaîne en mémoire que de lire un fichier encore et encore pour chaque caractère. Au moins sur ma machine: pastebin.com/zH5trQQs