将命令输出发送回管道中的前一个子 shell 进行处理
Send command output back to previous subshell in pipe for processing
给定 this html5 page,使用文件描述符与先前的子 shell 交互地 xmllint
处理它。
应用于 xml2xpath OS project.
如何重现:运行“问题”部分的脚本片段
基本命令是:
(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=([^=]+)/ =/;ta; s/^([0-9]{1,5}) *ELEMENT *([^ ]*)/ /; /^[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
...
问题
通过文件描述符将行发送回子 shell 不会正确连接行,namespace
信息出现在 arrns
数组(下一个代码示例)内它自己的项目上。
因此,从文件描述符读取并使用 sed
处理以填充数组未按预期工作。此外,尽量避免 post-在此阶段处理或解析文件超过 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=([^=]+)/ =/;ta; s/^([0-9]{1,5}) *ELEMENT *([^ ]*)/ /; /^[1-9]|namespace/ P;D }'
}
(
echo 'xpath //*'
echo "$stop"
IFS=$'\n' read -r -d '' -a arrns < <(parse_line && printf '[=13=]')
# 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
出于某种原因,此 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)/ /;ta }' \
-e 's/^([0-9]{1,5}) *ELEMENT *([^ ]*)/ /' \
-e 's/(default)? ?namespace( [a-z0-9]+)? ?href=([^=]+)/=/g' \
-e '/^[1-9]/ P;D'
这里的顺序似乎很重要
1- 按预期加入行
sed -E -e :a -e '/^[1-9]/,/^(default|namespace)/ { $!N;s/\n(default|namespace)/ /;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 ELEMENT html
之后:1 html
3- 处理第 1 步结果的 *namespace*
部分
-e 's/(default)? ?namespace( [a-z0-9]+)? ?href=([^=]+)/=/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
...
给定 this html5 page,使用文件描述符与先前的子 shell 交互地 xmllint
处理它。
应用于 xml2xpath OS project.
如何重现:运行“问题”部分的脚本片段
基本命令是:
(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=([^=]+)/ =/;ta; s/^([0-9]{1,5}) *ELEMENT *([^ ]*)/ /; /^[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
...
问题
通过文件描述符将行发送回子 shell 不会正确连接行,namespace
信息出现在 arrns
数组(下一个代码示例)内它自己的项目上。
因此,从文件描述符读取并使用 sed
处理以填充数组未按预期工作。此外,尽量避免 post-在此阶段处理或解析文件超过 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=([^=]+)/ =/;ta; s/^([0-9]{1,5}) *ELEMENT *([^ ]*)/ /; /^[1-9]|namespace/ P;D }'
}
(
echo 'xpath //*'
echo "$stop"
IFS=$'\n' read -r -d '' -a arrns < <(parse_line && printf '[=13=]')
# 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
出于某种原因,此 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)/ /;ta }' \
-e 's/^([0-9]{1,5}) *ELEMENT *([^ ]*)/ /' \
-e 's/(default)? ?namespace( [a-z0-9]+)? ?href=([^=]+)/=/g' \
-e '/^[1-9]/ P;D'
这里的顺序似乎很重要
1- 按预期加入行
sed -E -e :a -e '/^[1-9]/,/^(default|namespace)/ { $!N;s/\n(default|namespace)/ /;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 ELEMENT html
之后:1 html
3- 处理第 1 步结果的 *namespace*
部分
-e 's/(default)? ?namespace( [a-z0-9]+)? ?href=([^=]+)/=/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
...