检测 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;
}
我正在寻找一种最好的跨平台方式来从 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;
}