检测 Tcl 脚本是否在后台进程中 运行

Detect if a Tcl script is run in a background process

我正在寻找一种最好的跨平台方式来从 Tcl 脚本中检测解释器 运行 是在前台还是在后台进程中。

我已经通过 ps(或 Linux 上的 /proc/$$/stat)了解了如何做到这一点;有没有更好的方法,或者我是否必须围绕该方法破解一些东西?我已经有一个用 C 编写的实用程序库,因此公开 ps 也使用的低级别 API,这样我就不必解析进程输出(或特殊文件内容)就可以了。

没有真正跨平台的前景概念,但主要平台确实有办法做到这一点根据他们对前景的概念

Linux、macOS 和其他 Unix:

为了确定一个进程是否在前台,您需要检查它是否 process group ID is the terminal's controlling process group ID. For Tcl, you'd be looking to surface the getpgrp() and tcgetpgrp() system calls (both POSIX). Tcl has no built-in exposure of either, so you're talking either a compiled extension (may I recommend Critcl?)或调用外部程序,如 ps。幸运的是,如果你使用后者(如果这只是偶尔的操作,这是一个合理的选择)你通常可以调整输出,这样你就可以获得你想要的信息并且不需要解析。

# Tested on macOS, but may work on other platforms
proc isForeground {{pid 0}} {
    try {
        lassign [exec ps -p [expr {$pid ? $pid : [pid]}] -o "pgid=,tpgid="] pgid tpgid
    } on error {} {
        return -code error "no such process"
    }
    # If tpgid is zero, the process is a daemon of some kind
    expr {$pgid == $tpgid  &&  $tpgid != 0}
}

Windows

code to do it, and the required calls are supported by the TWAPI 扩展,因此您无需自己制作。 (警告!我没有测试过这个!)

package require twapi_ui

proc isForeground {{pid 0}} {
    set forground_pid [get_window_thread [get_foreground_window]]
    return [expr {($pid ? $pid : [pid]) == $foreground_pid}]
}

感谢 Donal,我想出了下面应该适用于所有 POSIX Unix 变体的实现:

/*
    processIsForeground

    synopsis: processIsForeground

    Returns true if the process is running in the foreground or false
    if in the background.
*/
int IsProcessForegroundCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    /* Check the arg count */
    if (objc != 1) {
        Tcl_WrongNumArgs(interp, 1, objv, NULL);
        return TCL_ERROR;
    }

    int fd;
    errno = 0;
    if ((fd = open("/dev/tty", O_RDONLY)) != -1) {
        const pid_t pgrp = getpgrp();
        const pid_t tcpgrp = tcgetpgrp(fd);
        if (pgrp != -1 && tcpgrp != -1) {
            Tcl_SetObjResult(interp, Tcl_NewBooleanObj(pgrp == tcpgrp));
            close(fd);
            return TCL_OK;
        }
        close(fd);
    }
    Tcl_SetErrno(errno);
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "processIsForeground: ", (char *)Tcl_PosixError(interp), NULL);
    return TCL_ERROR;
}

int Pextlib_Init(Tcl_Interp *interp)
{
    if (Tcl_InitStubs(interp, "8.4", 0) == NULL)
        return TCL_ERROR;
// SNIP
    Tcl_CreateObjCommand(interp, "processIsForeground", IsProcessForegroundCmd, NULL, NULL);

    if (Tcl_PkgProvide(interp, "Pextlib", "1.0") != TCL_OK)
        return TCL_ERROR;

    return TCL_OK;
}