Debe eliminar los caracteres de espacio en blanco del $IFSparámetro para readdejar de omitir los caracteres iniciales y finales (con -n1el carácter de espacio en blanco si alguno sería tanto inicial como final, por lo que se omite):
while IFS= read -rn1 a; do printf %s "$a"; done
Pero incluso entonces, bash readomitirá los caracteres de nueva línea, con los que puede trabajar:
while IFS= read -rn1 a; do printf %s "${a:-$'\n'}"; done
Aunque podría usar IFS= read -d '' -rn1en su lugar o incluso mejor IFS= read -N1(agregado en 4.1, copiado de ksh93(agregado o)), que es el comando para leer un carácter.
Tenga en cuenta que bash readno puede hacer frente a los caracteres NUL. Y ksh93 tiene los mismos problemas que bash.
Con zsh:
while read -ku0 a; do print -rn -- "$a"; done
(zsh puede hacer frente a caracteres NUL).
Tenga en cuenta que esos read -k/n/Nleen una cantidad de caracteres , no bytes . Entonces, para los caracteres multibyte, pueden tener que leer varios bytes hasta que se lea un carácter completo. Si la entrada contiene caracteres no válidos, puede terminar con una variable que contiene una secuencia de bytes que no forma caracteres válidos y que el shell puede contar como varios caracteres . Por ejemplo, en un entorno local UTF-8:
$ printf '\375\200\200\200\200ABC' | bash -c '
IFS= read -rN1 a; echo "${#a}"'
6
Eso \375introduciría un carácter UTF-8 de 6 bytes. Sin embargo, el sexto ( A) anterior no es válido para un carácter UTF-8. Todavía terminas con \375\200\200\200\200Ain $a, que bashcuenta como 6 caracteres, aunque los primeros 5 no son realmente caracteres, solo 5 bytes no forman parte de ningún carácter.
IFSen nada para que los espacios en blanco sobrevivan a la división de palabras.