Bash:如果出现问题,请进行链式命令替换

问题描述 投票:0回答:1

我想编写一个基于

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
正常工作?

bash dialog io-redirection command-substitution
1个回答
0
投票

根据上面评论中的提示,我可以想出一个解决方案,尽管并不完全令人满意,因为需要为每个复杂/嵌套命令定义

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"
© www.soinside.com 2019 - 2024. All rights reserved.