使用 bash read 逐个字符读取

使用 bash read 逐个字符读取

我一直在尝试使用 bash 逐字符读取文件。

经过多次尝试和错误,我发现这是有效的:

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

即,我可以逐行读取它,然后逐个字符地循环遍历每一行。

在这样做之前,我曾尝试过: exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done 但它会跳过文件中的所有空格

您能解释一下原因吗?有没有办法使第二种策略(即用 bash 的读取逐个字符读取)起作用?

答案1

您需要从$IFS参数中删除空格字符,以read停止跳过前导和尾随字符(使用 时-n1,空格字符(如果有)将同时是前导和尾随字符,因此会被跳过):

while IFS= read -rn1 a; do printf %s "$a"; done

但即便如此,bashread也会跳过换行符,您可以使用以下方法解决:

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 相同的问题。

使用 zsh:

while read -ku0 a; do print -rn -- "$a"; done

(zsh 可以处理 NUL 字符)。

请注意,那些read -k/n/N阅读了一些人物, 不是字节。因此,对于多字节字符,它们可能必须读取多个字节,直到读取完整的字符。如果输入包含无效字符,则最终可能会得到一个变量,该变量包含不形成有效字符的字节序列,并且 shell 最终可能会将其计数为几个人物。例如在 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\200Ain $a,它bash算作 6人物虽然前 5 个不是真正的字符,只是 5 个字节,不构成任何字符的一部分。

答案2

cut这是一个使用,for循环 & 的简单示例wc

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

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

不是吗?

相关内容