Вам нужно удалить пробельные символы из $IFS
параметра, read
чтобы прекратить пропускать начальные и конечные -n1
символы (с помощью символа пробела, если он есть, и начальный, и конечный символы, поэтому пропускаются):
while IFS= read -rn1 a; do printf %s "$a"; done
Но даже тогда bash's read
пропустит символы новой строки, с которыми вы можете обойти:
while IFS= read -rn1 a; do printf %s "${a:-$'\n'}"; done
Хотя вы можете использовать IFS= read -d '' -rn1
вместо этого или даже лучше IFS= read -N1
(добавлено в 4.1, скопировано из ksh93
(добавлено в o
)) команду, которая читает один символ.
Обратите внимание, что bash не read
может справиться с NUL-символами. И у ksh93 есть те же проблемы, что и у bash.
С зш:
while read -ku0 a; do print -rn -- "$a"; done
(Zsh может справиться с NUL-символами).
Обратите внимание, что они read -k/n/N
читают количество символов , а не байтов . Поэтому для многобайтовых символов им, возможно, придется читать несколько байтов, пока не будет прочитан полный символ. Если входные данные содержат недопустимые символы, вы можете получить переменную, которая содержит последовательность байтов, которая не образует допустимых символов и которую оболочка может в итоге считать за несколько символов . Например, в локали UTF-8:
$ printf '\375\200\200\200\200ABC' | bash -c '
IFS= read -rN1 a; echo "${#a}"'
6
Это \375
введет 6-байтовый символ UTF-8. Тем не менее, шестой ( A
) выше недействителен для символа UTF-8. Вы по-прежнему получаете \375\200\200\200\200A
in $a
, который bash
считается как 6 символов, хотя первые 5 из них на самом деле не являются символами, только 5 байтов не являются частью какого-либо символа.
IFS
ничего не нужно, чтобы пробелы выживали при разделении слов.