私はbashを使用してファイルを1文字ずつ読み取ろうとしています。
多くの試行錯誤の結果、これが機能することがわかりました。
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
つまり、1行ずつ読み取ってから、各行をcharごとにループできます。
これを行う前に、私は試しました:exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done
ですがファイル内のすべての空白をスキップします。
理由を教えてください。 2番目の戦略(つまり、bashの読み取りで1文字ずつ読み取る)を機能させる方法はありますか?
read
の$IFS
パラメータから空白文字を削除して、先頭と末尾の文字のスキップを停止する必要があります(-n1
を使用すると、空白文字が先頭と末尾の両方にある場合、スキップされます。 ):
while IFS= read -rn1 a; do printf %s "$a"; done
しかし、それでもbashのread
は改行文字をスキップします。改行文字は次のようにして回避できます。
while IFS= read -rn1 a; do printf %s "${a:-$'\n'}"; done
代わりにIFS= read -d '' -rn1
を使用することもできますが、IFS= read -N1
(4.1で追加、ksh93
からコピーしたもの(o
で追加))は、1文字を読み取るコマンドです。
Bashのread
はNUL文字に対応できないことに注意してください。また、ksh93にはbashと同じ問題があります。
Zshの場合:
while read -ku0 a; do print -rn -- "$a"; done
(zshはNUL文字に対応できます)。
これらのread -k/n/N
はbytesではなく、多数のcharactersを読み取ることに注意してください。したがって、マルチバイト文字の場合、完全な文字が読み取られるまで複数バイトを読み取らなければならない場合があります。入力に無効な文字が含まれている場合、有効な文字を形成しない一連のバイトを含む変数になり、シェルが文字として数える可能性があります。たとえば、UTF-8ロケールの場合:
$ printf '\375\200\200\200\200ABC' | bash -c '
IFS= read -rN1 a; echo "${#a}"'
6
その\375
は6バイトのUTF-8文字を導入します。ただし、上記の6番目(A
)は、UTF-8文字には無効です。あなたはまだ\375\200\200\200\200A
の$a
で終わります。これはbash
は6としてカウントされますcharactersただし、最初の5つは実際には文字ではなく、5バイトではありません任意のキャラクターの一部を形成します。
これは、cut
、for
ループ、およびwc
を使用した簡単な例です。
bytes=$(wc -c < /etc/passwd)
file=$(</etc/passwd)
for ((i=0; i<bytes; i++)); do
echo $file | cut -c $i
done
[〜#〜] kiss [〜#〜] でしょ?