【问题标题】:Bash 'Process substitution' - what is going on here?Bash '进程替换' - 这里发生了什么?
【发布时间】:2014-05-21 19:07:52
【问题描述】:

我正在使用进程替换来创建我编写的速记内联 XSL 函数...

function _quickxsl() {
    if [[ $1 == "head" ]] ; then
        cat <<'HEAD'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
    <!ENTITY apos "&#39;">
]>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    xmlns:func="http://exslt.org/functions"
    xmlns:kcc="http://www.adp.com/kcc"
    extension-element-prefixes="func kcc">
HEAD
    else
        cat <<'FOOT'
</xsl:stylesheet>
FOOT
    fi
}

function quickxsl() {
    {
        _quickxsl head && cat && _quickxsl foot
    } | xsltproc - "$@"
}

如果我提供真实文件作为 xsltproc 的参数,它似乎工作正常。另一方面,如果我用进程替换来调用它:

$ quickxsl <(cat xml/kconf.xml) <<QUICKXSL
QUICKXSL
warning: failed to load external entity "/dev/fd/63"
unable to parse /dev/fd/63

现在,我了解到管道路径正在提供给通过另一个管道 (xsltproc) 连接的子进程。于是稍微改写了一下:

function quickxsl() {
    xsltproc - "$@" < <( _quickxsl head && cat && _quickxsl foot )
}

似乎解决了一些问题

/dev/fd/63:1: parser error : Document is empty

^
/dev/fd/63:1: parser error : Start tag expected, '<' not found

^
unable to parse /dev/fd/63

知道为什么管道不能被继承吗?

更新:

如果我再次简化 quickxsl 函数:

function quickxsl() {
    xsltproc <( _quickxsl head && cat && _quickxsl foot ) "$@"
}

我遇到了同样的问题,但是通过一点 xtrace 很容易确定是哪个 fifo 导致了问题...

$ quickxsl <(cat xml/kconf.xml) <<QUICKXSL
QUICKXSL
+ quickxsl /dev/fd/63
++ cat xml/kconf.xml
+ xsltproc /dev/fd/62 /dev/fd/63
++ _quickxsl head
++ [[ head == \h\e\a\d ]]
++ cat
++ cat -
++ _quickxsl foot
++ [[ foot == \h\e\a\d ]]
++ cat
/dev/fd/62:1: parser error : Document is empty

^
/dev/fd/62:1: parser error : Start tag expected, '<' not found

^
cannot parse /dev/fd/62

本练习的目的是让“进程替换”管道连接到一个在其标准输出上返回 XML 的函数,该函数可以正常工作。如果我将内容写入文件并将其传递给函数,一切都很好。如果我使用进程替换,则子进程无法从管道中读取,并且管道似乎已关闭或无法访问。示例:

quickxsl <(my_soap_service "query") <<XSL
    <xsl:template match="/">
        <xsl:value-of select="/some/path/text()"/>
    </xsl:template>
XSL

如您所见,它提供了一些快捷方式。

更新:

很好的一点是管道不能连续打开或关闭。 xsltproc 的 Strace 输出显示它只打开文件一次。

$ grep /dev/fd !$
grep /dev/fd /tmp/xsltproc.strace
execve("/usr/bin/xsltproc", ["xsltproc", "/dev/fd/62"], [/* 31 vars */]) = 0
stat("/dev/fd/62", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
stat("/dev/fd/62", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
stat("/dev/fd/62", {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
open("/dev/fd/62", O_RDONLY)            = 3
write(2, "/dev/fd/62:1: ", 14)          = 14
write(2, "/dev/fd/62:1: ", 14)          = 14
write(2, "cannot parse /dev/fd/62\n", 24) = 24

天哪,我忽略了寻找:

read(3, "<?xml version=\"1.0\" encoding=\"UT"..., 16384) = 390
read(3, "", 12288)                      = 0
lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
lseek(3, 18446744073709547520, SEEK_SET) = -1 ESPIPE (Illegal seek)
read(3, "", 4096)                       = 0

【问题讨论】:

  • 问题是什么?!
  • 很抱歉,当我按 Enter 键时,它会发布问题而不是插入新行。
  • 你从不使用fifos,更不用说命名的了。您正在使用流程替换和 HERE 文档
  • 好的。我已经更新了这个问题。想必你知道答案了吧?
  • xsltproc替换为strace -o xsltproc.strace xsltproc,然后查看日志文件(xsltproc.strace),看xsltproc是否多次打开和关闭文件。如果是这样,那么它与无法多次读取的管道和 FIFO(和终端)等设备不兼容。您可能需要稍微修改命令行以获取嵌入到日志文件名中的进程名称。

标签: bash fifo


【解决方案1】:

似乎我在 xsltproc 中发现了一个错误。它不识别 FIFO 文件类型,并在读取文档后尝试寻找 FIFO 文件描述符。我提出了一个错误。一种解决方法是解析 FIFO 文件类型的 xsltproc 参数,并将它们转换为 xsltproc 可以查找的临时文件。

https://bugzilla.gnome.org/show_bug.cgi?id=730545

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-04
    • 1970-01-01
    • 2013-11-03
    • 2023-04-10
    • 1970-01-01
    • 2010-12-14
    相关资源
    最近更新 更多