【发布时间】:2020-05-31 19:37:55
【问题描述】:
TL;DR - 转到 UPDATE 以获取玩具示例。以下是我最初的问题,然后我能够在一个小示例中重现该问题,为清晰起见进行了编辑。
我不明白这里发生了什么。我一直在努力想弄清楚为什么我用 perl 编写的测试总是失败。
我正在测试一个在打开输出日志文件失败时关闭日志记录的功能,这样就不会抑制到 STDERR 的输出。为了测试它是否有效,我动态创建了一个文件并将其 chmod 为不可写。生成的代码运行应将打开错误与所有其他 STDERR 一起打印到 STDERR 输出。在运行测试之前,我在反引号中执行 chmod。
但是,发生的事情是日志文件不断被写入而没有错误 - 所以测试失败了。我开始通过在各个地方打印ls -halF 的输出来进行完整性检查,以查看权限更改发生的位置。最初的 chmod 似乎正在工作:
`echo > file.log;chmod 444 file.log;ls -halF file.log`
随后的ls -halF file.log 在进行了一些其他设置后确认了预期的权限,但是在我运行实际测试代码时很早的一行代码说所有者有写权限!
我最终发现了一个解决问题的方法——通过使用 perl chmod() 方法而不是反引号中的 chmod,这让我觉得发生了一些异步的事情——这对我来说毫无意义。
我最终尝试了,因为在我的众多测试中,我偶尔会看到一个测试随机成功。所以我写了一个快速循环来一遍又一遍地执行相同的测试 - 并发现它随机成功的几率可能有 30%!这就是让我认为这个问题可能存在某种异步性的原因,我无法解释,因为我认为反引号基本上是在等待进程完成,因为它返回输出 - 那么这里发生了什么?没看懂。
为了完整性 - 实际的测试命令也在反引号中完成 - 它运行一个独立的脚本。
在下面的更新中,我展示了一个不同的案例,但它展示了权限状态的随机性...
更新
好的。这是一个演示问题的小型独立示例。注意每种情况下第二行的输出不同...
deleteme1.pl
#!/usr/bin/perl
print(`echo '' > deleteme.log;chmod 444 deleteme.log`);
print("1: ",`ls -l deleteme.log`);
`chmod 777 deleteme.log;rm -f deleteme.log`;
deleteme2.pl
#!/usr/bin/perl
print(`echo '' > deleteme.log;chmod 444 deleteme.log`);
print("2: ",`ls -l deleteme.log`);
chmod 0777, "deleteme.log";
unlink("deleteme.log");
现在观察这个重复循环的输出:
tcsh> foreach f ( `seq 1 30` )
perl deleteme1.pl
perl deleteme2.pl
end
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -rw-r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -rw-r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -rw-r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r--@ 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
1: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
2: -r--r--r-- 1 robleach staff 1 May 31 21:01 deleteme.log
请注意,所有者权限差异始终来自 deleteme1.pl。我认为@ 表示 macOS 上的扩展属性。这可能与它有关,但在某些情况下 xattr 存在,但没有权限差异......
而且我也怀疑这个问题可能与此有关:
>ls -1 t | wc -l
46391
所以有些事情是异步发生的——这到底是什么?!会不会和 SIP 或者 csr(util) 有关系?
更新 2
这是一个更符合我的具体情况的示例。
deleteme3.pl
这个代表我的修复:
#!/usr/bin/perl
open(TLG,">deleteme.log");
print TLG '';
close(TLG);
chmod 0444, "deleteme.log";
if(open(TEST,">deleteme.log"))
{print "3 opened\n"}
else
{print "3 unable to open\n"}
chmod 0777, "deleteme.log";
unlink("deleteme.log");
deleteme4.pl
这个演示了原来的问题。
#!/usr/bin/perl
`echo '' > deleteme.log;chmod 444 deleteme.log`;
if(open(TEST,">deleteme.log"))
{print "4 opened\n"}
else
{print "4 unable to open\n"}
chmod 0777, "deleteme.log";
unlink("deleteme.log");
我循环了这两个,如上:
tcsh>foreach f ( `seq 1 1000` )
perl deleteme3.pl
perl deleteme4.pl
end
在 1000 次尝试中,3 unable to open 始终为 deleteme3.pl 输出。 deleteme4.pl输出4 opened 4次,其余为4 unable to open。
显然,这并不能说明我在大多数情况下看到的频率,我的权限显示为所有者可写,但我猜我遇到了某种甜蜜点,我的文件权限出现了问题。
我很想看看是否有人可以在任何平台上重现这些结果,但我在运行系统 perl 版本 5.18.2 的 macOS Sierra (10.12.6) 上。
【问题讨论】:
-
没有研究工作?我花了几个小时在这上面,并且已经使用 perl 20 年了。谁在不发表评论的情况下投了反对票?反引号应该等到命令完成:“执行命令,命令完成后继续执行您的 perl 脚本。” fibrevillage.com/scripting/…
-
Re "我认为反引号实际上是在等待进程完成,因为它返回输出",确实如此。以上执行
/bin/sh,参数为/bin/sh、-c和echo > file.log;chmod 444 file.log;ls -halF file.log。它会等待sh返回,然后再继续。正如你所说,它必须收集孩子的输出。这是否回答你的问题?如果不是,我不知道它是什么。 -
不可能说出测试失败的原因,因为您只展示了您认为重要的部分 - 但实际上可能不是重要的部分或并非所有重要的部分。请参阅How to create a Minimal, Reproducible Example。
-
我猜你看到的异步可能是 macOS GUI,但你只是顺便提到了 Mac。所有失败的案例似乎都添加了扩展属性,因此当该版本的文件碰巧存在时,可能碰巧访问了 Mac(Spotlight 等)的某些内容。但是你为什么要为这些东西掏腰包?如果你在 shell 脚本中做同样的事情会发生什么?
-
如果你想主动拒绝帮助,你为什么要问这个问题?
标签: perl asynchronous chmod