【问题标题】:Send command output back to previous subshell in pipe for processing将命令输出发送回管道中的上一个子 shell 进行处理
【发布时间】:2024-01-14 21:53:01
【问题描述】:

给定this html5 page,使用xmllint 使用文件描述符与先前的子shell 交互地处理它。
申请于xml2xpath OS project

如何重现:在“问题”部分运行脚本 sn-p

基本命令是

(echo 'xpath //*'; echo "bye") | xmllint --shell html5.html

提供要处理的源输出:

/ > xpath //*
Object is a Node Set :
Set contains 346 nodes:
1  ELEMENT html
    default namespace href=http://www.w3.org/1999/xhtml
    ATTRIBUTE lang
      TEXT
        content=en
    ATTRIBUTE dir
      TEXT
        content=ltr
2  ELEMENT head
3  ELEMENT title
...
202  ELEMENT div
    default namespace href=http://www.w3.org/1999/xhtml
203  ELEMENT p
204  ELEMENT code
205  ELEMENT math
    default namespace href=http://www.w3.org/1998/Math/MathML
...
345  ELEMENT mo
346  ELEMENT mn
/ > bye

目标是将包含namespace 的行加入上一行,将n ELEMENT name 显示为n name,忽略其余部分(并向xmllint 发送更多命令)。
以下命令给出正确的行预计会出现在以前的子shell

(echo 'xpath //*' )| xmllint --shell $proj/git/xml2xpath/tests/resources/html5.html | \
sed -nEe '{ :a; $!N;s/^([0-9]{1,5}) *ELEMENT *([^ ]*)\n +(default)? ?namespace ([a-z]+)? ?href=([^=]+)/\1 \2 \3\4=\5/;ta; s/^([0-9]{1,5}) *ELEMENT *([^ ]*)/\1 \2/; /^[1-9]/ P;D }'

1 html default=http://www.w3.org/1999/xhtml
2 head
3 title
4 link
5 link
6 link
7 link
8 body
9 h1
10 h2
...

问题
通过文件描述符将行发送回子外壳不会正确连接行,namespace 信息出现在 arrns 数组内的自己的项目上(下一个代码示例)。
因此,从文件描述符中读取并使用sed 处理以填充数组无法按预期工作。此外,在此阶段尽量避免对文件进行超过 1 次的后处理或解析。

目前最好的方法是:

#!/bin/bash
wget --no-clobber "https://www.w3.org/TR/XHTMLplusMathMLplusSVG/sample.xhtml" -O html5.html

fname='xff'
[ ! -p "$fname" ] && mkfifo "$fname"
exec 3<>"$fname"

cat /dev/null > tmp.log

stop='dir xxxxxxx'

function parse_line(){
    while read -r -u 3 xline; do 
        printf "%s\n" "$xline"
        if [ "$xline" == "/ > $stop" ]; then 
            break 
        fi
    done | sed -nEe '{ :a; $!N;s/^([0-9]{1,5}) *ELEMENT *([^ ]*)\n +(default)? ?namespace ([a-z]+)? ?href=([^=]+)/\1 \2 \3\4=\5/;ta; s/^([0-9]{1,5}) *ELEMENT *([^ ]*)/\1 \2/; /^[1-9]|namespace/ P;D }'
}

(
    echo 'xpath //*'
    echo "$stop"
    IFS=$'\n' read -r -d '' -a arrns < <(parse_line && printf '\0')
    
    # print to file temporarily for debugging and avoid sending to xmllint shell 
    printf "%s\n" "${arrns[@]}" >> tmp.log
    echo "OUT OF LOOP 1 ${#arrns[@]}" >> tmp.log
    echo "bye"
) | xmllint --shell html5.html >&3

exec 3>&-
rm xff
cat tmp.log

将 fd 3 中的所有行解析为一个变量,然后应用 sed 得到相同的结果。

tmp.log 上显示arrns 的内容(几乎正确):

1 html
default namespace href=http://www.w3.org/1999/xhtml
2 head
3 title
4 link
5 link
...
239 math
default namespace href=http://www.w3.org/1998/Math/MathML
...
OUT OF LOOP 1 354

示例中的第 1 行和第 239 行应该是这样的

239 math default=http://www.w3.org/1998/Math/MathML

这可能允许通过一些处理将此命令从同一子 shell 转发到 xmllint,以设置它们在文档中出现的命名空间。

setns default=http://www.w3.org/1998/Math/MathML

【问题讨论】:

  • 您的问题到底是什么? MCVE 肯定会有所帮助。
  • @RenaudPacalet 谢谢,感谢您的评论。包含namespace 的行未连接到前一行。另一方面,问题解释了问题并提供了重现的代码。对某些人来说可能不是最低要求,但它肯定是完整且可重现的。

标签: bash sed file-descriptor xmllint


【解决方案1】:

由于某种原因,此 while read 循环无法模拟 xmllint 输出,并使原始 sed 命令失败

while read -r -u 3 xline; do ... ; done

修复了循环后的sed命令,现在输出正确

sed -E -e :a -e '/^[1-9]/,/^(default|namespace)/ { $!N;s/\n(default|namespace)/ \1/;ta }' \
-e 's/^([0-9]{1,5}) *ELEMENT *([^ ]*)/\1 \2/' \
-e 's/(default)? ?namespace( [a-z0-9]+)? ?href=([^=]+)/\1\2=\3/g' \
-e '/^[1-9]/ P;D'

这里的顺序似乎很重要

1- 按预期加入行
sed -E -e :a -e '/^[1-9]/,/^(default|namespace)/ { $!N;s/\n(default|namespace)/ \1/;ta }'

1  ELEMENT html
default namespace href=http://www.w3.org/1999/xhtml

变成

1  ELEMENT html default namespace href=http://www.w3.org/1999/xhtml

2- 处理*ELEMENT*
-e 's/^([0-9]{1,5}) *ELEMENT *([^ ]*)/\1 \2/'

之前:1 ELEMENT html
之后:1 html

3- 处理*namespace* 第 1 步结果的一部分
-e 's/(default)? ?namespace( [a-z0-9]+)? ?href=([^=]+)/\1\2=\3/g'

之前:1 html default namespace href=http://www.w3.org/1999/xhtml
之后:1 html default=http://www.w3.org/1999/xhtml

4- 打印以数字开头的行
-e '/^[1-9]/ P;D'

错误的结果

1 html
default namespace href=http://www.w3.org/1999/xhtml
2 head
...
191 svg:svg
namespace svg href=http://www.w3.org/2000/svg
...

修正后的正确结果

1 html default=http://www.w3.org/1999/xhtml
2 head
...
191 svg:svg svg=http://www.w3.org/2000/svg
...

【讨论】: