为什么这个 if 语句强制 zenity 的 --auto-close 立即关闭?



为我正在开发的一个小应用程序设置了一个Debian包,这与问题无关,但对于某些上下文,该应用程序是一个简单的bash脚本,它在本地机器上部署了一些docker容器。但我想添加一个依赖性检查,以确保系统在尝试做任何事情之前都有docker。如果没有,下载它,如果有,忽略它。我认为在它旁边有一个小的zenity对话框来显示正在发生的事情是很好的。

在这个过程中,出于明显的原因,我在开始之前检查互联网,出于某种原因,如果zenity有--auto-close标志,我检查是否有互联网的方式将立即关闭整个进度块。

这里有一个小的伪例子,如果语句是从我的代码中直接复制粘贴的,那么其他的都是填充。:

#!/bin/bash
condition=0
if [[ $condition ]]; then
(
echo "0"
# Check for internet
if ping -c 3 -W 3 gcr.io; then
echo "# Internet detected, starting updates..."; sleep 1
echo "10"
else            
err_msg="# No internet detected. You may be missing some dependencies. 
Services may not function as expected until they are installed."
echo $err_msg
zenity --error --text="$err_msg"
echo "100"
exit 1
fi

echo "15"
echo "# Downloading a thing" ; sleep 1
echo "50" 
if zenity --question --text="Do you want to download a special thing?"; then
echo "# Downloading special thing" ; sleep 1
else
echo "# Not downloading special thing" ; sleep 1
fi
echo "75" 
echo "# downloading big thing" ; sleep 3
echo "90"
echo "# Downloading last thing" ; sleep 1
echo "100" 
) | 
zenity --progress --title="Dependency Management" --text="downloading dependencies, please wait..." 
--percentage=0 --auto-close
fi

所以我真的只是想知道为什么这会让zenity抓狂。若你们注释掉那个If语句,一切都如你们所愿,一旦它达到100,zenity进度屏幕就会关闭。如果保留If语句,但删除自动关闭标志,它将按预期执行。这就像它在100初始化,然后转到0才能正常进行。但如果是这样的话,自动关闭永远不会起作用,但在他们在帮助部分给你的这个小例子中,它运行得很好。https://help.gnome.org/users/zenity/stable/progress.html.en

感谢您的益智游戏!剧透已经结束了,但我认为在我探讨这个问题时,回头看看可能会有所帮助。️如果你对答案比对旅程更感兴趣,可以随意滚动。无论如何,我永远不会知道。

根据我自己的建议(见问题下面的第一条评论),我开始创建一个小的、独立的、完整的例子。但是,正如他们在技术支持中所说:在调试问题之前,你需要调试客户。(无意冒犯;我是一个可怕的目击者,除非我提前知道有人需要重现我发现的问题。)

我把你关于查看互联网的评论理解为";它在我添加CCD_ 1之前起作用;因此,最明智的做法似乎是注释掉代码的这一部分。。。然后就成功了!那么,当添加ping时,会发生什么不同的情况呢?

时间的改变是没有意义的,所以问题一定是ping生成的输出通过管道传输到zenity。因此,我更改了命令,将其输出重定向到位桶:

ping -c 3 -W 3 gcr.io&>/dev/null;

这也奏效了!有趣的

我探索了几个小洞:

  • 我从命令行运行ping,并通过od -xa管道输出,以检查是否有奇怪的控制字符,但没有
  • 我没有将ping0块的内容括在括号中((),它在子shell中执行命令,而是尝试在同一shell中使用大括号({})执行命令。不,再一次
  • 我尝试了一堆其他令人尴尬的无用和耗时的想法。不,不,不

然后我意识到我可以做

ping -c 3 -W 3 gcr.io | zenity --progress --auto-close

直接从命令行。有了--auto-close标志就失败了,但没有它就正常工作。天哪,这简化了事情!这大约是";最小的";尽你所能。但事实并非如此:我通过将ping的输出重定向到一个文件中,用完了当天剩下的所有情报点,所以我可以只进行

(cat output; sleep 1) | zenity --progress --auto-close

直到我终于弄清楚了这件事,才一直取笑可怜的gcr.io。(sleep给了我足够的时间来查看弹出窗口,因为当管道在输入结束时关闭时,zenity就会退出。那么,output文件中有什么?

PING gcr.io (172.253.122.82) 56(84) bytes of data.
64 bytes from bh-in-f82.1e100.net (172.253.122.82): icmp_seq=1 ttl=59 time=18.5 ms
64 bytes from bh-in-f82.1e100.net (172.253.122.82): icmp_seq=2 ttl=59 time=21.8 ms
64 bytes from bh-in-f82.1e100.net (172.253.122.82): icmp_seq=3 ttl=59 time=21.4 ms
--- gcr.io ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 18.537/20.572/21.799/1.449 ms

魔法zenity杀手一定在那里的某个地方!剩下的一切(哈,">All"!)就是让我的";最小的";通过删除文件的片段直到它停止中断,示例变得更小。然后我会把上次删除的东西放回去,然后我删除了其他东西,da capo,令人作呕,或者至少ad minimus。(或者其他什么;我不会说拉丁语。)最终文件缩减为

64 bytes from bh-in-f82.1e100.net (172.253.122.82): icmp_seq=1 ttl=59 time=18.5 ms

我从一开始就开始删除东西。最终我发现,不管线路的长度如何,只要它以一个不是0的数字开头,并且其中至少有3位数字,它就会断开。如果确实以0开头并且其中至少有4位数字,它也会中断。。。除非第二个数字也是0!更重要的是,句号会让它变得更奇怪:句号之后的任何数字都不会让它断裂,无论它们是什么。

然后,然后来了啊哈!片刻zenity文件中写道:

Zenity逐行从标准输入中读取数据。如果一条线前缀为#的文本将使用该行的文本进行更新。如果行中只包含一个数字,百分比将更新为数字

哇,真的吗?不可能有那么可笑吧?

我找到了zenity的源,下载了它,提取了它(使用tar -xf zenity-3.42.1.tar.xz),打开了progress.c,并找到了检查"是否"的函数;一行只包含一个数字"只有当行中的第一个字符是数字时,才会调用函数。

108 static float
109 stof(const char* s) {
110         float rez = 0, fact = 1;
111         if (*s == '-') {
112                 s++;
113                 fact = -1;
114         }
115         for (int point_seen = 0; *s; s++) {
116                 if (*s == '.' || *s == ',') {
117                         point_seen = 1;
118                         continue;
119                 }
120                 int d = *s - '0';
121                 if (d >= 0 && d <= 9) {
122                         if (point_seen) fact /= 10.0f;
123                         rez = rez * 10.0f + (float)d;
124                 }
125         }
126         return rez * fact;
127 }

你看到了吗?在这里,我给你一个sscce,并评论:

// Clear the "found a decimal point" flag and iterate
// through the input in `s`.
115   for (int point_seen = 0; *s; s++) {
// If the next char is a decimal point (or a comma,
// for Europeans), set the "found it" flag and check
// the next character.
116     if (*s == '.' || *s == ',') {
117       point_seen = 1;
118       continue;
119     }
// Sneaky C trick that converts a numeric character
// to its integer value. Ex: char '1' becomes int 1.
120     int d = *s - '0';
// We only care if it's actually an integer; skip anything else.
121     if (d >= 0 && d <= 9) {
// If we saw a decimal point, we're looking at tenths,
// hundredths, thousandths, etc., so we'll need to adjust
// the final result. (Note from the peanut gallery: this is
// just ridiculous. A progress bar doesn't need to be this
// accurate. Just quit at the first decimal point instead
// of trying to be "clever." 
122       if (point_seen) fact /= 10.0f;
// Tack the new digit onto the end of the "rez"ult.
// Ex: if rez = 12.0 and d = 5, this is 12.0 * 10.0 + 5. = 125.
123       rez = rez * 10.0f + (float)d;
124     }
125   }
// We've scanned the entire line, so adjust the result to account
// for the decimal point and return the number.
126   return rez * fact;

现在你看到了吗?

作者决定";[i] 如果一行只包含一个数字"0";通过检查(仅!)第一个字符是数字。如果是,那么它会提取所有的数字(如果有小数点的话,还有第一个小数点),将它们混合在一起,并返回它发现的任何东西,忽略它可能看到的任何其他东西。

因此,当然如果有3位数字,而第一位不是0,或者如果有4位数字,但前2位不是0……因为3位数字总是至少为100,一旦进度达到100或更高,zenity就会--auto-close


扰流板:

ping语句生成的输出混淆了zenity,使其认为进度已达到100%,因此它关闭了对话框。

顺便说一句,祝贺你:你发现了程序员可能犯的最新手的错误之一。。。这不是你的bug!无论出于何种原因,zenity的作者决定推出他们自己的函数,将一行文本转换为浮点数,但它根本不会像医生所说的那样,也不会像任何普通人所期望的那样

如果你能想出如何报告错误,你可以获得一堆因果报应积分,如果你以修复的形式提交报告,你将获得奖金。️

相关内容

  • 没有找到相关文章

最新更新