如何在Shell脚本内重定向全量输出并保留终端交互?
问题:如何在整体重定向输出的脚本中保留终端交互能力
需求说明
- 需要向终端输出反馈(已尝试
| tee /dev/tty) - 需要支持多种用户输入方式:
read -p 'prompt: ' varselect yn in "Yes" "No"; do ...- 私有仓库
git clone时的账号密码输入
当前测试脚本(精简版)
#/bin/bash set -e set -x { if lxc list | grep container then : else lxc launch images:ubuntu/20.04 container echo 'waiting for networking to be up-and-running...' | tee /dev/tty sleep 5 echo 'installing base system...' | tee /dev/tty lxc exec container -- apt update lxc exec container -- apt install -y git echo "install documentation processor? " | tee /dev/tty select yn in "Yes" "No" do if [ $yn == "Yes" ] then echo 'installing documentation processor...' | tee /dev/tty lxc exec container -- apt install -y wget lxc exec container -- wget https://github.com/plantuml/plantuml/releases/download/v1.2022.12/plantuml-1.2022.12.jar -O /usr/local/bin/plantuml.jar fi break done read -p 'enter your EMAIL address: ' email | tee /dev/tty lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git config --global user.email "$email" read -p 'enter your FULL NAME: ' name | tee /dev/tty lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git config --global user.name "$name" lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git config --global credential.helper store echo 'cloning repository...' | tee /dev/tty lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git clone git@github.com:gabime/spdlog.git echo "enable audio in container? " | tee /dev/tty select yn in "Yes" "No" do if [ $yn == "Yes" ] then for dev in $(find /dev/snd -type c) do lxc config device add container snd_$(basename $dev) unix-char source=$dev path=$dev gid=1000 lxc exec container -- apt install -y alsa-utils done fi break done fi } >lxd_build.log 2>&1
现存问题
read -p的提示仅写入日志,终端不可见select的交互提示仅写入日志,终端不可见git clone的账号密码输入提示终端不可见
解决方案
核心思路
放弃脚本整体重定向的方式,改为区分非交互命令输出与终端交互流:非交互命令的输出定向到日志,交互相关的提示/输入直接绑定到终端,同时用tee保留终端反馈。
具体修复步骤
- 处理
read -p提示read -p的提示默认输出到stderr,会被整体重定向吞掉。改为显式将提示输出到终端,再单独读取输入,最后把输入内容写入日志:
# 替代原read写法 echo -n 'enter your EMAIL address: ' > /dev/tty read email echo "EMAIL address entered: $email" >> lxd_build.log
- 处理
select交互select依赖终端环境,需要临时将其输入输出绑定到终端,同时手动记录选择结果到日志:
# 替代原select写法 echo "install documentation processor? " > /dev/tty # 绑定select的IO到终端 select yn in "Yes" "No" do if [ "$yn" = "Yes" ] then echo 'installing documentation processor...' | tee -a lxd_build.log # 非交互命令输出定向到日志 lxc exec container -- apt install -y wget >> lxd_build.log 2>&1 lxc exec container -- wget https://github.com/plantuml/plantuml/releases/download/v1.2022.12/plantuml-1.2022.12.jar -O /usr/local/bin/plantuml.jar >> lxd_build.log 2>&1 fi # 记录选择结果到日志 echo "Selected option: $yn" >> lxd_build.log break done < /dev/tty > /dev/tty 2>&1
- 处理
git clone交互
SSH/HTTPS方式的git clone交互需要绑定终端IO,同时可以用tee兼顾日志记录:
# 替代原git clone写法 echo 'cloning repository...' | tee -a lxd_build.log lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git clone git@github.com:gabime/spdlog.git 2>&1 | tee -a lxd_build.log
优化后的完整脚本
#!/bin/bash set -e set -x LOG_FILE="lxd_build.log" > "$LOG_FILE" # 清空日志,如需追加改为>> if lxc list | grep container then : else lxc launch images:ubuntu/20.04 container >> "$LOG_FILE" 2>&1 echo 'waiting for networking to be up-and-running...' | tee -a "$LOG_FILE" sleep 5 echo 'installing base system...' | tee -a "$LOG_FILE" lxc exec container -- apt update >> "$LOG_FILE" 2>&1 lxc exec container -- apt install -y git >> "$LOG_FILE" 2>&1 # 处理第一个select交互 echo "install documentation processor? " > /dev/tty select yn in "Yes" "No" do if [ "$yn" = "Yes" ] then echo 'installing documentation processor...' | tee -a "$LOG_FILE" lxc exec container -- apt install -y wget >> "$LOG_FILE" 2>&1 lxc exec container -- wget https://github.com/plantuml/plantuml/releases/download/v1.2022.12/plantuml-1.2022.12.jar -O /usr/local/bin/plantuml.jar >> "$LOG_FILE" 2>&1 fi echo "Selected option for documentation processor: $yn" >> "$LOG_FILE" break done < /dev/tty > /dev/tty 2>&1 # 处理email输入 echo -n 'enter your EMAIL address: ' > /dev/tty read email echo "enter your EMAIL address: $email" >> "$LOG_FILE" lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git config --global user.email "$email" >> "$LOG_FILE" 2>&1 # 处理姓名输入 echo -n 'enter your FULL NAME: ' > /dev/tty read name echo "enter your FULL NAME: $name" >> "$LOG_FILE" lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git config --global user.name "$name" >> "$LOG_FILE" 2>&1 lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git config --global credential.helper store >> "$LOG_FILE" 2>&1 # 处理git clone echo 'cloning repository...' | tee -a "$LOG_FILE" lxc exec container --user 1000 --group 1000 --cwd /home/ubuntu --env HOME=/home/ubuntu -- git clone git@github.com:gabime/spdlog.git 2>&1 | tee -a "$LOG_FILE" # 处理第二个select交互 echo "enable audio in container? " > /dev/tty select yn in "Yes" "No" do if [ "$yn" = "Yes" ] then for dev in $(find /dev/snd -type c) do lxc config device add container snd_$(basename $dev) unix-char source=$dev path=$dev gid=1000 >> "$LOG_FILE" 2>&1 done lxc exec container -- apt install -y alsa-utils >> "$LOG_FILE" 2>&1 fi echo "Selected option for audio enable: $yn" >> "$LOG_FILE" break done < /dev/tty > /dev/tty 2>&1 fi
关键说明
- 不再使用整体大括号重定向,而是逐个处理命令输出,确保交互流独立于日志流
tee -a $LOG_FILE实现终端反馈与日志记录的双重输出- 所有交互操作的IO都绑定到
/dev/tty,保证提示和输入正常显示 - 手动记录用户输入内容到日志,确保日志完整性
内容的提问来源于stack exchange,提问作者ZioByte
相关产品推荐
相关产品推荐

