web-dev-qa-db-ja.com

bashで文字を1文字ずつ読み取る

私は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文字ずつ読み取る)を機能させる方法はありますか?

8
PSkocik

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/Nbytesではなく、多数の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バイトではありません任意のキャラクターの一部を形成します。

12

これは、cutforループ、およびwcを使用した簡単な例です。

bytes=$(wc -c < /etc/passwd)
file=$(</etc/passwd)

for ((i=0; i<bytes; i++)); do
    echo $file | cut -c $i
done

[〜#〜] kiss [〜#〜] でしょ?

2
Gilles Quenot