如何检查Perl脚本是否在终端运行?

9
我正在尝试在Linux上的Perl脚本中确定它是否在终端上运行。
也就是说,我需要代码:
- 仅在命令行上运行时返回true。 - 在运行./myscript.pl | less甚至是./myscript.pl </dev/null >/dev/null 2>/dev/null时也返回true。 - 在cron作业或CGI脚本中运行时返回false。
特别是由于第二个要求,我不能使用-t STDOUT和变体,IO::Interactive也没有用。
这些信息似乎是可用的。如果我运行ps,它会在列中显示类似pts/2的条目,即使我运行./myscript.pl </dev/null >/dev/null 2>/dev/null,并且在作为cron作业或CGI脚本运行时显示?

有没有一种优雅的方法可以在Perl脚本中确定这一点? 我不想解析ps的输出。


我认为Perl有可用的isatty(3)函数。 - Keith
1
isatty 存在于 POSIX 模块中,但是和 -t 一样,只是检查文件句柄是否连接到 tty。这不是我需要的... - mscha
ctermid 函数是否可用? - Keith
ctermid确实存在于POSIX模块中。不幸的是,它在命令行和cron作业中都返回/dev/tty/ - mscha
那么有没有一种不使用tty检测的替代方法呢?在你的crontab中放置一个特殊的环境变量,然后让脚本检查它。 - Keith
@Keith:是的,我可以这样做;但我已经有几种可靠的方法来检测tty,所以我会坚持使用它们。 :) - mscha
5个回答

12
您可以尝试打开/dev/tty。 如果您在终端中(甚至在远程计算机上的终端)运行,则此方法可行。 否则,如果通过at或cron运行脚本,则不起作用。
注意:这仅适用于Unix系统。

谢谢。这似乎可以解决问题: sub isatty() { no autodie; return open(my $tty, '+<', '/dev/tty'); } - mscha
@mscha - 关闭这个文件描述符怎么样?根据你运行的频率,你可能会用完文件描述符。 - Ingo
6
@Ingo,这并不必要,因为他使用了一个词法文件句柄并没有返回它。因此,当变量超出作用域 (即isatty返回时),文件句柄将自动关闭。 - cjm
3
如果你需要经常运行这个程序,可以使用更高效的版本:sub isatty() { no autodie; state $isatty = open(my $tty, '+<', '/dev/tty'); return $isatty; }。我选择使用这个版本。 - mscha

5

我自己问题的另一个答案。我研究了ps源代码,看它是如何确定TTY的,它使用了/proc/[pid]/stat

use strict;
use warnings;
use 5.010;
use autodie;

sub isatty()
{
    # See http://www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html
    open(my $stat, '<', "/proc/$$/stat");
    if (<$stat> =~ m{^\d+\s+\(.*\)\s+\w\s+\d+\s+\d+\s+\d+\s+(\d+)}) {
        return $1 > 0;
    }
    else {
        die "Unexpected format in /proc/$$/stat";
    }
}

1

PS应该能帮到你。
ps aux | grep 'filename.pl'


3
那正是我想要避免的事情......另外,仅仅使用文件名进行搜索并不是非常可靠。 - mscha
1
这并没有解决OP所遇到的问题。 - codeforester

0
为了部分回答我的问题,以下代码可以解决:
sub isatty()
{
    my $tty = `/bin/ps -p $$ -o tty --no-headers`;
    $tty =~ s{[\s?]}{}g;
    return $tty;
}

返回TTY名称(如果有)(为真),否则返回""(为假)。

我仍然更喜欢没有外部命令的解决方案...


0

首先,您必须检查输出是否与终端相关联,可以通过 -t 参数实现。当然,如果您想要,您可以查看 /proc/$pid/fd/1 ,它是设备的符号链接。您可以测试它是否为终端。

但如果这还不够,您可以通过 %ENV 特殊哈希表来检查环境变量。CGI-BIN 接口会设置其中一些变量。如果您在 cron 下运行脚本,则会设置一些变量。如果这还不够,您可以在 /etc/crontab 文件中设置并在脚本中进行测试。具体操作取决于您的需求。

您应该仅调用完整过程一次。您不能迭代它,因为脚本环境不会改变,直到它停止工作。

您不需要调用任何外部命令,也不需要特殊库。您所需要的只是 Windows 不兼容性。但如果您使用的是 Windows10,则它具有类似于基于 Ubuntu 的 Linux 的环境。在此情况下,您无法像在 win32api 和类 Unix 系统之间进行兼容性时那样强制执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接