建立 tee 的流程替换列表

建立 tee 的流程替换列表

我有一个像这样的脚本,我用它来将命令广播到连接到多个不同服务器的 postgresql 数据库 CLI 的多个实例。我正在使用一组硬编码的进程替换。

#!/bin/bash
# names have been changed to protect the guilty
cred="user=dbadmin password=SECRET"
domain=example.com

tee \
   >( psql -X "host=db1.$domain dbname=db1 $cred" ) \
   >( psql -X "host=db2.$domain dbname=db2 $cred" ) \
   >( psql -X "host=db3.$domain dbname=db3 $cred" ) \
   >( psql -X "host=xdb1.$domain dbname=xdb1 $cred" ) \
   > /dev/null    
wait

我想做的是使用 for 循环构建替换数组并将该数组传递给 tee,如下所示:

tee "${p[@]}" > /dev/null

但是当我使用循环时,我得到了每个项目,$p因为/dev/fd/63tee 给了我每个项目的错误。

tee: /dev/fd/63: No such file or directory

示例非工作代码:

p=()
for z in db1 db2 db3 xdb1
do
  p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )
done
tee "${p[@]}" > /dev/null

有办法让这项工作发挥作用吗?

答案1

发生这种情况是因为在这条线上:

  p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )

...bash 认为该行是一个完整的命令。当进行进程替换时,被替换进程的 STDIN 在命令完成时关闭。

我认为只有两种方法可以做到这一点:

  1. eval。我们不要去那里。
  2. exec。让我们去那里吧:

 

p=()
for z in db1 db2 db3 xdb1
do
  exec {fd}> >(psql -X "host=$z.$domain dbname=$z $cred")
  p+=( $fd )
done
cd /dev/fd && exec tee "${p[@]}" >/dev/null

{fd}>语法使 bash 分配一个新的文件描述符,并将其值赋给$fd,然后将其推入$p
现在$p是一堆文件描述符编号,我们必须tee写入其中,因此我们cd/dev/fd文件描述符编号写入实际文件,然后调用tee.

(还有很多其他的剥猫皮的方法,但这是我想到的第一个也是最简单的方法)

答案2

这是我在评论中的意思的一个简单例子。

for 循环(每个 psql 命令重复直到成功,在后台子 shell 中运行)。不需要生成的进程替换文件描述符的数组。

cred='user=dbadmin password=SECRET'
domain='example.com'

tf=$(mktemp)
cat > "$tf"

for z in db1 db2 db3 xdb1 ; do
  ( while ! psql -X "host=$z.$domain dbname=$z $cred" < "$tf" ; do : ; done) &
done > /dev/null

# don't delete the tempfile until all jobs have completed
wait

rm -f "$tf"

可以选择使用sleep x而不是:while循环中,以便在每次重试尝试之间有一个小的延迟。

更智能的版本会在 while 循环内执行更多操作,检查退出状态和/或 grep stderr 以找出任何失败的原因并做出适当的响应。

相关内容