Bash 进程替换
使用进程替换将命令输出作为文件参数传递,构建强大的 shell 管道。
进程替换基础
# <(cmd) — 将命令输出视为文件(读取)
diff <(sort file1.txt) <(sort file2.txt)
comm <(sort a.txt) <(sort b.txt)
wc -l <(find . -name "*.go")
# >(cmd) — 将命令输入视为文件(写入)
tee >(gzip > out.gz) >(wc -l) > /dev/null
# 实际应用:比较远程和本地文件
diff <(ssh host cat /etc/hosts) /etc/hosts
# 多重替换
join <(sort file1) <(sort file2)
Here-String & Here-Doc
# Here-string: <<< "字符串"
grep 'pattern' <<< "在这个字符串中搜索"
base64 -d <<< "aGVsbG8="
read -r a b c <<< "one two three"
# Here-doc: << 分隔符
cat << EOF
第一行
第二行: $variable(会展开)
EOF
# 缩进 here-doc(<<-)
if true; then
cat <<- EOF
缩进内容(行首 tab 会被去除)
EOF
fi
# 引号分隔符 — 不展开变量
cat << 'EOF'
$NOT_EXPANDED \n 字面量
EOF
命名管道(FIFO)
# 创建命名管道
mkfifo /tmp/mypipe
# 终端 1:写入端
echo "hello" > /tmp/mypipe
# 终端 2:读取端
cat < /tmp/mypipe
# 持久日志管道
mkfifo /tmp/log_pipe
tee /tmp/log_pipe | logger -t myapp &
your_program > /tmp/log_pipe
# 清理
rm /tmp/mypipe
命令分组
# ( ) — 子 shell 分组(变更不影响父 shell)
(cd /tmp && ls) # 执行后 pwd 不变
(export VAR=val; ./script) # VAR 不影响父 shell
# { } — 当前 shell 分组
{ echo "a"; echo "b"; } | grep a
{ cd /tmp && ls; } # cd 影响当前 shell(注意空格和分号)
# 重定向整组输出
{ cmd1; cmd2; cmd3; } > output.txt 2>&1
# 将分组结果管道给 while
{ echo "line1"; echo "line2"; } | while read line; do
echo "Got: $line"
done
实用管道
# 同时记录到多个目标
./app 2>&1 | tee >(grep ERROR > errors.log) >(grep WARN > warns.log) | cat
# 进程替换并行处理
paste <(cut -f1 file) <(cut -f2 file | tr 'a-z' 'A-Z')
# 检查两个命令输出是否相同
if diff <(cmd1) <(cmd2) > /dev/null; then
echo "输出相同"
fi
# 单独捕获 stderr
exec 3>&1
stderr=$(./script 2>&1 1>&3)
exec 3>&-
# 管道错误处理
set -o pipefail # 任意管道命令失败则返回非零
cmd1 | cmd2 | cmd3