Bash函数:将命令的输出重定向到文件,但仍将函数中的printf输出打印到标准输出

编程语言 2026-07-12

我有这么一个函数:

execute_command_with_progress_bar()
{
    printf "START\n"
    "$@" &

    local pid="$!"

    printf "LOOP\n"

    # Signal 0 just tests whether the process exists
    while kill -0 "$pid" 2> /dev/null
    do
        printf "o"
        sleep 1
    done

    printf "END\n"
}

如果我这样调用它:

    execute_command_with_progress_bar ssh root@"$DEV_IP_ADDR" ls >> ./results.log 2>&1"

那么所有输出都会进入 results.log(远程 ls 的内容),以及 execute_command_with_progress_bar 函数中的所有printf调用。

我需要怎么修改,才能让远程ls的输出进入文件,而函数中的printf输出打印到标准输出?

解决方案

重新审视你的问题,看来你只需要把终端输出放到STDERR:

execute_command_with_progress_bar() {
    printf "START\n" >&2
    "$@" &

    local pid="$!"
    printf "LOOP\n" >&2

    while kill -0 "$pid" 2> /dev/null; do
        printf "o" >&2
        sleep 1
    done

    printf "END\n" >&2
}

如果你想把输出既存储起来又打印到终端,你需要对它们进行多路输出。

让一个函数同时允许对STDERR与 STDOUT的重定向

并且仍然在终端输出,如 你对这个注释的请求

为此,你需要打开 另一个 文件描述符,指向 STDOUT

请看这个

# bash source file for defining "execute_command_with_progress_bar" function

[[ $__execProgressFD ]] && [[ -t $__execProgressFD  ]] ||
    exec {__execProgressFD}>&1

execute_command_with_progress_bar() {
    printf "START\n" >&$__execProgressFD
    "$@" &

    local pid="$!"
    printf "LOOP\n" >&$__execProgressFD

    while kill -0 "$pid" 2> /dev/null; do
        printf "o" >&$__execProgressFD
        sleep 1
    done
    wait $pid
    printf "END\n" >&$__execProgressFD
}

然后:

$ . exec_progress.sh 
$ execute_command_with_progress_bar >/tmp/testfile.log 2>&1 \
    sh -c "sleep 2;ls -ld /tmp /tnt;sleep 2"
START
LOOP
ooooEND

$ cat /tmp/testfile.log 
[1] 2228115
ls: cannot access '/tnt': No such file or directory
drwxrwxrwt 41 root root 868352 Mar  9 12:17 /tmp
[1]+  Done                    "$@"

或者这个,用于一个真实的旋转指示器...

# bash source file for defining "execute_command_with_spinner" function

[[ $__execProgressFD ]] && [[ -t $__execProgressFD  ]] ||
    exec {__execProgressFD}>&1

__execProgressSpinnerShapes=(  ⢎\   ⠎⠁  ⠊⠑  ⠈⠱  \ ⡱  ⢀⡰  ⢄⡠  ⢆⡀  )

execute_command_with_spinner() {
    printf "START\n" >&$__execProgressFD
    "$@" &

    local pid="$!" pnt
    printf "LOOP\e[?25l\n" >&$__execProgressFD # cursor invisible
    while kill -0 "$pid" 2> /dev/null; do
    # Save cursor position, render frame, restore cursor position
        printf >&$__execProgressFD '\e7%b\e8' \
           "${__execProgressSpinnerShapes[pnt++ %
                   ${#__execProgressSpinnerShapes[@]}]}"
    read -rsn 1 -t .05 _
    done
    wait $pid
    printf "\e[?25h\rEND\n" >&$__execProgressFD # cursor visible
}

注意:如果在GNU/Linux下运行,最好使用 /proc 伪文件系统,而不是为每次循环都fork到 kill。将 kill -0 替换为:

    while [[ -d /proc/$pid ]]; do

但如果你想在打印时同时存储输出

为此,你可以使用 tee 命令:

logfile="path/to/logfile.log"
exec 1> >(tee /dev/tty >>"$logfile")

然后发往 STDOUT 的所有内容都会被追加到 $logfile,并同时发送到当前终端。

更多示例和信息在

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章