我想编写一个基于
dialog programbox
(或其他)的脚本,并捕获命令的 stderr
和传递给 stdout
的 dialog
到数组(如果有)中,以及命令的退出状态(如最后一个数组元素)。为此,我使用准备好的命令字符串 command substitution
和 fd3 redirection
。
基本上,只要没有涉及
if
的链式命令,这就能很好地工作 - 然后我会收到错误消息,例如在本例中为“command not find”。这是一个(简化的)示例:
# Prepare command strings
declare cmd0='if [[ -e . ]]; then echo TRUE; fi'
cmd1=($("$cmd0") 2>&1)
cmd2=(dialog --colors --no-collapse --programbox "\Zb\Z1List local folders..." 20 160)
# Redirect stdout and stderr to dialog, while executing cmd1 > cmd2, capturing cmd1 exit status
exec 3>&1
tmp=($(${cmd1[@]} 2>&1 | "${cmd2[@]}" 1>&3; echo ${PIPESTATUS[0]}))
exec 3>&-
# Print results
printf '%s\n' "${tmp[@]}"
命令声明似乎需要数组声明,但需要特殊字符,例如“;”也会加剧问题。这就是我尝试过的;直接使用
c
而不是 cmd1
会将整行打印为错误。
需要更改什么才能使 cmd1 中的链接命令(包括
command substituion
)的 if
正常工作?
根据上面评论中的提示,我可以想出一个解决方案,尽管并不完全令人满意,因为需要为每个复杂/嵌套命令定义
functions
来解析/传递给 dialog programbox
。
dialog_programbox
可以通过参数接受和解析简单命令,或者,对于复杂命令,处理来自 stdin
的输入:
可能有一种方法可以将管道输入提取到捕获的结果变量中(此处:exitStatus),例如: G。根据
tee
的使用 - 如果有人知道如何使用,请随时发布解决方案。
对于其他
dialog
变体,它们接受来自 var 的输入并且不消耗 stdin
,dialog_programbox
的输出可以作为换行符分隔的字符串打印到 stdout
,退出/管道状态作为最后一个或第一个元素,以便于从捕获的结果变量中轻松分割。稍后我可能会相应地扩展解决方案。
# Function example for complex commands to pipe to dialog_programbox()
complexCmdFunc1() {
# Requires explicit stderr redirection to stdout for each command
# Could also split cmd execution and store exit status in var for reporting
if ! git worktree remove -- "$1" 2>&1; then
echo -e "\n\nREMOVAL BLOCKED:" "Worktree may not exist or is unlcean/locked (try force) - ABORTING."
# Need to explicitly return exit status to report
return 1
fi
echo -e "Successfully removed worktree:\n$1"
return 0
}
dialog_programbox() {
local -i height
local -i width
local -n cmd
local cmdTmp
local title
for arg; do
if [[ $arg == +([[:digit:]]) ]]; then
if [[ $height -gt 0 ]]; then
width=$arg
continue
fi
height=$arg
elif [[ $title ]]; then
if [[ -R $arg ]]; then
cmd=$arg
# For direct CMD arg injection, has bug
elif ! declare -p "$arg" 2>&1 1>/dev/null; then
cmdTmp=$arg
cmd=cmdTmp
elif [[ $(declare -p "$arg" 2>/dev/null) == 'declare -a'* ]]; then
cmd=$arg
elif [[ -z $cmd ]]; then
cmd=$arg
fi
else
title="$arg"
fi
done
[[ -z $height ]] && height=30
[[ -z $width ]] && height=120
# CMD ARG INJECTION
if [[ $cmd ]]; then
$( echo "${cmd[@]}" ) 2>&1 | dialog --title "$title" --colors --no-collapse --programbox $height $width 1>&3
echo ${PIPESTATUS[0]}
# PIPING STDIN
else
# stdin command substitution - would work but swallow pipe/exit status
#dialog --title "$title" --colors --no-collapse --programbox $height $width 1>&3 < <($( echo ${cmd[@]} ) 2>&1)
dialog --title "$title" --colors --no-collapse --programbox $height $width </dev/stdin 1>&3
# Temp var would address empty stdin but waste real-time progress report
#local tmp="$(grep . </dev/stdin)"
# if [[ $tmp ]] ; then
# dialog --title "$title" --colors --no-collapse --programbox $height $width <<<"$tmp"
# else
# echo 'Nothing to process.' | dialog --title "$title" --colors --no-collapse --programbox $height $width
# fi
fi
}
# COMPLEX CMD output piping usage - requires explicit echo of PIPESTATUS
exitStatus=$(complexCmdFunc1 'unreleased' | dialog_programbox '\Zb\Z1Deleting local worktree...' 20 160; echo ${PIPESTATUS[0]})
# Empty pipe, will render an empty dialog, of which addressing would require storing cmd result in temp var and losing real-time progress reporting
#exitStatus=$(echo | dialog_programbox '\Zb\Z1Deleting local worktree...' 20 160; echo ${PIPESTATUS[0]})
# CMD var injection examples (for simple/non-nested commands)
#declare -a c=(ls foo bar)
#declare c='ls foo bar'
#exitStatus=$(dialog_programbox '\Zb\Z1Deleting local worktree...' c 20 160)
# direct CMD injection - HAS BUG
#exitStatus=$(dialog_programbox '\Zb\Z1Deleting local worktree...' 'ls foo bar' 20 160)
echo -e "EXIT_STATUS: $exitStatus"