【问题标题】:old Linux Kernel weird behavior旧的 Linux 内核奇怪的行为
【发布时间】:2014-06-06 07:44:25
【问题描述】:

我有一台带有最小定制 linux 内核的旧设备。大约 20 年前,我们从一家公司购买了该设备,并对其进行了编程以满足我们的需求。

uname -r
2.4.21

我在设备上开发了一个基于文本的 UI,并允许通过 telnet 和串行端口访问它。

使用 telnet 连接时,两次调用 read -s STR 会导致第二次读取被忽略。这是因为 telnet 以"\r\n" 结束行。

例如:

echo "Enter new password: "; read -s password1
echo "Enter new password again: "; read -s password2 #<--this read returns directly

所以我的解决方案是在每次读取调用后从标准输入读取所有字符。 但是,仅使用串行通信将"\n" 作为行尾发送。所以我决定在上面的读取调用中增加 1 秒的超时时间。

所以,我基本上是这样结束的:

echo "Enter new password: "; read -s password1
read -t 1 -n 1000 ignore
echo "Enter new password again: "; read -s password2
read -t 1 -n 1000 ignore

令我惊讶的是,read -t 1 有时会阻塞。所以我运行了以下内容来说明这种行为:

set -x

while true;
do
read -t 1 -n 10000 STR
echo "timeout"
sleep 5
done

脚本在 2 次循环后挂起,只有在我按 enter 时才会继续。

最后我尝试了以下方法:

set -x
unset STR
while true;
do
read -s -n 1 char
if [[ $char == $'\r' ]]
then
read -s -n 1 ignore
break
elif [[ $char == $'\n' ]]
then
break
fi
STR=$STR$char
done

这导致:

++ true
++ read -s -n 1 char
 ]][[ a == \
++ [[ a == \
 ]]
++ STR=a
++ true
++ read -s -n 1 char   <-------- HERE I PRESSED ENTER
 ]][[ '' == \
++ [[ '' == \
 ]]
++ STR=a
++ true
++ read -s -n 1 char

所以ENTER"\n" 被转换为'',因此它不会跳出循环。

在较新的设备上,我们可以更好地控制内核

uname -r
2.6.24.6

该问题不会发生,即调用 read 两次不会导致第二次 read 调用被忽略。

有没有人想办法解决这个问题?

【问题讨论】:

  • 这听起来有些不对劲。首先,由于该对是\r\n,因此第一次读取应该在存储在参数中的字符串末尾包含\r,而不是需要第二次读取来清除它。其次,虽然 telnet 协议可能使用\r\n,但不应将它们传递给通过 telnet 接收其输入的底层 shell。
  • @chepner 确实很奇怪。在我看来,\r 包含在内部字段分隔符 (IFS) 中。但是,由于内核中缺乏对基本系统命令的支持,我无法打印IFS。我试过export IFS=' \t\n',但无济于事。顺便说一句,除了[[ $char == $'\r' ]] || [[ $char == $'\n' ]],还有其他方法可以测试\r\n 吗?或者甚至打印以十六进制接收的字符?
  • 我认为内核版本根本不重要。你能告诉我们你正在使用的 shell 和它的版本吗?
  • @mkalkov GNU bash,版本 2.05.8(1)-release (i386-redhat-linux-gnu)

标签: linux bash timeout


【解决方案1】:

终端将每个 '\r' 翻译成 '\n',所以你看不出两者之间的区别。您可以使用 stty igncr 完全忽略 CR。有了这个,你的第一种方法应该有效:

stty igncr
echo "Enter new password: "; read -s password1
echo "Enter new password again: "; read -s password2
stty sane

最后一个stty sane 可能不是必需的。

如果该方法不起作用,请尝试改用 stty -icrnl(但这样一来,$password1$password2 将在末尾有一个 \r;您可以使用 tr -d '\r' 抑制它)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    • 2019-04-01
    • 2021-03-16
    相关资源
    最近更新 更多