Tcl C API:将嵌入式 Tcl interp 的 stdout 重定向到文件而不影响整个程序

2024-01-01

#include <tcl.h>
int main(int argc, char** argv)
{
    Tcl_Interp *interp = Tcl_CreateInterp();

    Tcl_Channel stdoutChannel = Tcl_GetChannel(interp, "stdout", NULL);
    Tcl_UnregisterChannel(interp, stdoutChannel);

    Tcl_Channel myChannel = Tcl_OpenFileChannel(interp, "/home/aminasya/nlb_rundir/imfile", "w", 0744);

    Tcl_RegisterChannel(interp, myChannel);
    Tcl_Eval(interp, "puts hello");
}

在此代码中,我尝试关闭标准输出通道并将其重定向到文件。 (正如所描述的获取 Tcl C 过程的输出 https://stackoverflow.com/questions/14324357/get-the-output-from-tcl-c-procedures)。运行后,创建了“imfile”,但它是空的。我做错了什么?

我见过如何将 stdout 重定向到 tcl 中的文件 https://stackoverflow.com/questions/8531031/how-can-i-redirect-stdout-into-a-file-in-tcl也是,但我需要使用 Tcl C API 来完成。

我也尝试过这种方法,但还是没有结果。

FILE *myfile = fopen("myfile", "W+");
Tcl_Interp *interp = Tcl_CreateInterp(); 
Tcl_Channel myChannel = Tcl_MakeFileChannel(myfile, TCL_WRITABLE);
Tcl_SetStdChannel(myChannel, TCL_STDOUT);

您的情况的困难在于 Tcl 解释器的标准通道与主程序(和 C 运行时)所看到的标准流的文件描述符 (FD) 之间的交互,以及以下语义:open(2)在Unix中。

使所有输出重定向的过程如下所示:

  1. 操作系统确保在程序开始执行时三个标准文件描述符 (FD) 已打开(编号为 0、1 和 2,其中 1 为标准输出)。

  2. 一旦您创建的 Tcl 解释器初始化其三个标准通道(当您调用Tcl_GetChannel()对于“stdout”,如上所述here http://www.tcl.tk/man/tcl8.6/TclLib/StdChannels.htm),它们与主程序中已经存在的三个 FD 相关联。

    请注意,底层 FD 不是克隆的,相反,它们只是从封闭程序“借用”的。事实上,我认为在 99% 的情况下这是明智的做法。

  3. 当您关闭(注销时发生)标准频道时stdout在您的 Tcl 解释器中,底层 FD (1) 也已关闭。

  4. 致电给fopen(3)内部调用open(2)它获取最低的空闲 FD,即 1,因此主程序(和 C 运行时)所理解的标准输出流现在连接到该打开的文件。

  5. 然后,您从文件中创建一个 Tcl 通道并将其注册到解释器中。频道确实变成了stdout对于口译员来说。

最后,对主程序中的标准输出流的写入和对 Tcl 解释器中的标准输出通道的写入都会发送到相同的底层 FD,因此最终会出现在同一个文件中。

我可以看到两种方法来处理这种行为:

  • 使用一个巧妙的技巧将 FD 1“重新连接”到最初打开的同一流,并使文件为 Tcl 解释器打开stdout使用大于 2 的 FD。
  • 不要先让 Tcl 解释器初始化其标准通道,然后重新初始化其中一个通道,而是在让自动激活机制启动之前手动初始化所​​有通道。

两种方法都有其优点和缺点:

  • “保留 FD 1”通常更容易实现,并且如果您想重定向only stdout在你的 Tcl 解释器中,并让其他两个标准通道连接到封闭程序使用的相同标准流,这种方法似乎是明智的采用。可能的缺点是:

    • 涉及太多魔法(建议对代码进行大量注释)。
    • 不确定这在 Windows 上如何工作:没有dup(2)那里(见下文)并且可能需要一些其他方法。
    • Not使用标准流stdin and stderr随附的程序可能会有用。
  • 手动初始化 Tcl 解释器中的标准通道需要更多代码和据推测保证正确的排序(stdin, stdout, stderr, 以该顺序)。如果您希望 Tcl 解释器中的其余两个标准通道连接到封闭程序的匹配流,则此方法需要更多工作;第一种方法是免费的。

以下是如何保存 FD 1 以仅使stdout在 Tcl 解释器中连接到文件;对于封闭程序 FD 1 仍然连接到操作系统设置的同一流。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <tcl.h>

int redirect(Tcl_Interp *interp)
{
        Tcl_Channel chan;
        int rc;
        int fd;

        /* Get the channel bound to stdout.
         * Initialize the standard channels as a byproduct
         * if this wasn't already done. */
        chan = Tcl_GetChannel(interp, "stdout", NULL);
        if (chan == NULL) {
                return TCL_ERROR;
        }

        /* Duplicate the descriptor used for stdout. */
        fd = dup(1);
        if (fd == -1) {
                perror("Failed to duplicate stdout");
                return TCL_ERROR;
        }

        /* Close stdout channel.
         * As a byproduct, this closes the FD 1, we've just cloned. */
        rc = Tcl_UnregisterChannel(interp, chan);
        if (rc != TCL_OK)
                return rc;

        /* Duplicate our saved stdout descriptor back.
         * dup() semantics are such that if it doesn't fail,
         * we get FD 1 back. */
        rc = dup(fd);
        if (rc == -1) {
                perror("Failed to reopen stdout");
                return TCL_ERROR;
        }

        /* Get rid of the cloned FD. */
        rc = close(fd);
        if (rc == -1) {
                perror("Failed to close the cloned FD");
                return TCL_ERROR;
        }

        /* Open a file for writing and create a channel
         * out of it. As FD 1 is occupied, this FD won't become
         * stdout for the C code. */
        chan = Tcl_OpenFileChannel(interp, "aaa.txt", "w", 0666);
        if (chan == NULL)
                return TCL_ERROR;

        /* Since stdout channel does not exist in the interp,
         * this call will make our file channel the new stdout. */
        Tcl_RegisterChannel(interp, chan);

        return TCL_OK;
}
int main(void)
{
        Tcl_Interp *interp;
        int rc;

        interp = Tcl_CreateInterp();
        rc = redirect(interp);
        if (rc != TCL_OK) {
                fputs("Failed to redirect stdout", stderr);
                return 1;
        }
        puts("before");
        rc = Tcl_Eval(interp, "puts stdout test");
        if (rc != TCL_OK) {
                fputs("Failed to eval", stderr);
                return 2;
        }
        puts("after");

        Tcl_Finalize();

        return 0;
}

构建和运行(在 Debian Wheezy 中完成):

$ gcc -W -Wall -I/usr/include/tcl8.5 -L/usr/lib/tcl8.5 -ltcl main.c
$ ./a.out 
before
after
$ cat aaa.txt 
test

正如你所看到的,字符串“test”输出puts转到文件,同时字符串“before”和“after”,它们是write(2)n 到封闭程序中的 FD 1 (这就是puts(3)最后)去终端。

手动初始化方法将是这样的(有点伪代码):

Tcl_Channel stdin, stdout, stderr;

stdin = Tcl_OpenFileChannel(interp, "/dev/null", "r", 0666);
stdout = Tcl_OpenFileChannel(interp, "aaa.txt", "w", 0666);
stderr = Tcl_OpenFileChannel(interp, "/dev/null", "w", 0666);
Tcl_RegisterChannel(interp, stdin);
Tcl_RegisterChannel(interp, stdout);
Tcl_RegisterChannel(interp, stderr);

不过我还没有测试过这种方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tcl C API:将嵌入式 Tcl interp 的 stdout 重定向到文件而不影响整个程序 的相关文章

  • 在golang中将chan转换为non chan

    是否可以让函数funcWithNonChanResult有如下接口 func funcWithNonChanResult int 如果我想让它使用函数funcWithChanResult与接口 func funcWithChanResult
  • Rstudio tcltk 包的特定问题

    我正在尝试编译一个提示用户输入的交互式代码 我将 tcltk 包用于各种对话框 一切似乎都工作正常 直到今天它停止工作 应该注意的是 a 我没有在配置方面进行任何更改 b 它只是在 RStudio 中停止工作 但在 R 中仍然可以工作 假设
  • 如何使用 Swig 将枚举值从 TCL 脚本传递到 C++ 类

    我正在使用以下代码 1 文件 example i module example Put header files here or function declarations like below include example h incl
  • TCL - 按任意数量的空格将字符串拆分为列表

    假设我有一个像这样的字符串 set str AAA B C DFG 142 56 现在我想得到一个列表 如下所示 AAA B C DFG 142 56 为此 我想使用 split 函数 但在这种情况下我会得到一些额外的空列表 我怎样才能得到
  • tcl 脚本从具有相同格式的多个文本文件中提取值并生成基于列的报告

    我有从 Seg Y 文件创建的输出日志 例子 TRACE HEADER MIN MAX MEAN COUNT Trace sequence number within line 001 004
  • 循环提示输入另一个密码时出现问题

    我需要一些关于 EXPECT 脚本的帮助 我正在尝试在访问大量主机之前自动登录 并在用户错误输入密码时进行处理 我首先获取用户名和密码 然后针对特定主机对其进行验证 如果密码无效 我想循环并再次询问用户名和密码 我正在尝试这个 省略前面几行
  • 如何在tcl中执行子字符串提取和替换

    我正在尝试从 Tcl 中的字符串中提取子字符串 我编写了代码并且能够做到这一点 但我想知道是否还有其他有效的方法来做到这一点 所以确切的问题是我有一个字符串 name ext 10a string 10a string string str
  • WCF 最大消息大小配额

    我正在尝试调用 WCF 服务 托管在 Windows 服务中 而不是 IIS 但收到以下错误 最大消息大小配额 传入消息已超出 对于远程通道 请参阅 服务器日志以获取更多详细信息 我尝试将 MaxReceivedMessageSize 和
  • 在 Go 中将事物通道作为接口通道传递

    我的程序有一个管道结构 我刚刚实现了一个缓存过滤器 如果已处理的数据版本在缓存中 则该过滤器将直接将内容发送到输出 func Run in chan downloader ReadyDownload chan CCFile out make
  • 文件名中 TCL 通配符/glob 的使用

    我有这样的代码 proc myProc first last for set i first i lt last incr i set i cur PlainText i
  • 调试 tcl 流程 - 就像 bash -

    我只是想知道是否有类似的东西set x因为它存在于 Bash forTCL 中 它允许我跟踪脚本流程 然后帮助我调试它 Simple trace add execution source enterstep apply cmd op put
  • 打印大型 TCL 项目的源层次结构

    因此 我正在处理一个大型 TCL 项目 并且认为构建一个树状视图来显示文件在项目中的来源方式会很酷 我修改了源命令以执行以下操作 rename source real source proc source args set file han
  • 将channel_priority设置为“strict”是否会影响所有Conda环境?

    我使用 R 创建了一个 Conda 环境the 康达锻造公司频道说明 https conda forge org docs user introduction html how can i install packages from con
  • 执行 tail -F 直到匹配模式

    我想做一个tail F在文件上直到匹配模式 我找到了一种使用方法awk 但恕我直言 我的命令并不是很干净 问题是我need由于某些限制 只能用一行来完成 tail n 0 F tmp foo awk W interactive if 1 E
  • 将 csv 数据写入命名空间内的矩阵时,TCL 抛出无效命令名称

    这是一个奇怪的问题 我似乎无法弄清楚 我正在使用 TCL 8 5 我正在尝试使用以下命令将数据从 CSV 文件读取到矩阵中csv read2matrix命令 然而 每次我这样做时 它都会说我试图写入的矩阵是无效命令 我正在做的事情的片段 p
  • 如何计算位图的平均 RGB 颜色值

    在我的 C 3 5 应用程序中 我需要获取位图的红色 绿色和蓝色通道的平均颜色值 最好不使用外部库 这可以做到吗 如果是这样 怎么办 提前致谢 尝试让事情变得更精确 位图中的每个像素都有一定的 RGB 颜色值 我想获取图像中所有像素的平均
  • Django Channels/Daphne 内部服务器错误“‘dict’对象不可调用”

    我在连接到我的网站时收到此错误 因为它正在运行频道 2018 03 25 20 59 19 049 ERROR http protocol Traceback most recent call last File home virtuale
  • 如何在 {expect} 脚本中创建“循环”语句?

    假设是 这是脚本 usr bin expect set a test 我想在这个脚本中创建一个循环 以便它可以打印值 a 前面有一个基于循环的数字 所以如果我想让它循环3次 最终产品将变成 1 test 2 test 3 test 您可以使
  • (Tcl/Expect) 退出后清屏

    我想在退出我的 半 交互式期望脚本后清除屏幕 在本地计算机上 我可以在脚本中做到这一点吗 这是我尝试过的 但失败了 usr bin expect f set env TERM vt100 spawn ssh Y username domai
  • 与通道相比,sync.WaitGroup 的优势是什么?

    我正在开发一个并发 Go 库 我偶然发现了 goroutine 之间两种不同的同步模式 其结果相似 等待组 https play golang org p ZYPLlcp16TZ package main import fmt sync t

随机推荐