在bash使用read时遇到的问题

bash中的read命令是读文件和读用户输入时常能够用的指令,但它有一个坑,如果在写bash中不注意的话,很容易出问题。

简化下问题:

例如,如下这个bash:

while read line
do
        echo $line
        read -p "do you understand ?(y/n)" input
        case $input in
                [yY][eE][sS]|[yY])
                        echo "YES"
                ;;
                *)
                        echo "NO"
                ;;
        esac
done < test.txt

很简单,读test.txt,问下用户,读取输入

test.txt假设是如下几行:

question 1
question 2
question 3

但是执行结果并不尽如人意:

[root@localhost home]# sh test.sh
question 1
NO
question 3
NO


问题的原因也比较明显,在while中使用read来读取文件,循环中再用read来读取用户输入的时候, read的缓冲区估计是同一个,文件的内容冲掉了用户输入的内容。

因此既看不到提示输入,又得不到正确的结果……


至于怎么解决……我还没找到……先记录一下问题……


//===========================================================================

牛人解析


1. bash中的while read LINE并不是一次读入整个文件, 而是open文件拿到fd之后, 在while的每一次中, read都读满缓冲区, 然后lseek记录读到的底一个\n偏移量位置, 然后write这一段内容, 进行下一次while
2. 第一次read是读了1\n2\n3\n4\n5\n, 然后指针移动从第0位移动到-8位, 然后write这一段的内容1\n
3. 正常情况下, 第二次读, 会读2\n3\n4\n5\n, 然后指针从0移动到-6位, 然后write这一段内容2\n, 以此类推
4. 在while read中还内嵌了read的时候, 第二次的读, 就变成了while中取用户输入的read, 也即是, 把test.txt第一个\n后第二个\n前的内容读成了用户输入. 然后继续while的过程.

也即是, 
while read line
do
    echo $line
done < FILE

这段代码实际运行中的伪代码是:
while NOT_EOF_OF_FILE
do
    read "$line直到\n, 放到输出缓冲区" 
    write "输出缓冲区的内容"
done

因此, 在循环中加入read用户输入的代码,
while read line
do
    echo $line  ## @1
    read -p "user input: " input  ## @2
    echo $input  ## @3
done < FILE

这段代码实际运行中的伪代码是:
while NOT_EOF_OF_FILE
do
    ## @1
    read "$line直到\n, 放到输出缓冲区"
    write "输出缓冲区的内容"
    ## @2
    read "$line直到\n, 并当作用户输入, 放到输出缓冲区"
    ## @3
    write "输出缓冲区的内容"
done


所以, 假设文本test的内容是:
1
2
3
4
5

则正常输出:
1
2
3
4
5

当while中又重新read时的输出:
1  --> 这一行来自于test文件的第一个\n在第一个while中被read的结果
2  --> 这一行来自于test文件的第二个\n在第一个while中被当作用户输入被读取了
3  --> 这一行来自于test文件的第三个\n在第二个while中被read的结果
4  --> 这一行来自于test文件的第四个\n在第二个while中被当作用户输入被读取了
5  --> 这一行来自于test文件的第五个\n在第三个while中被read的结果

也就是每次while过程中, 都两次read了test的内容. 也即是, 本来5行内容, 被3次while读完了.


因为读取用户输入必须用到read, 不能替换, 所以只能把读取文件的read替换成别的方法. 因此, 可以这样规避这个问题:
for lines in $(/bin/awk '{print $0}' test)
do
    echo $lines
    read -p "Please input your name: " name
    echo $name
done



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值