提问人:Mega-X 提问时间:7/28/2023 更新时间:8/7/2023 访问量:64
BASH:将 stdin 重定向到可变数量的子进程
BASH: redirect stdin to variable number of subprocesses
问:
正如标题所说,我想重定向到可变数量的输出子进程。stdin
如果我必须重定向到输出文件,我可以做类似的事情
files=(file_1 file_2 ... file_n)
tee ${files[*]} >/dev/null
但是对于子流程(特别是使用流程替换),可以做类似的事情
programs=(">(exe_1 args_1)" ... ">(exe_n args_n)")
tee ${programs[@]} >/dev/null
不会将 as 进程替换作为文本文件名(出于安全原因,我假设);此外,具有替换的标志被解释为 的标志。>()
tee
是否可以从中读取一行并将其重定向到所有这些进程(同样,这些进程在数量上是可变的:未知)?我是否在某个地方错过了什么?stdin
n
提前致谢,对不起我的英语不好。
答:
2赞
Charles Duffy
7/28/2023
#1
不幸的是,这是一份工作。给出类似的东西:eval
all_redirections=""
add_redirection() {
local argv_q
printf -v argv_q '%q ' "$@" # generate an eval-safe escaping of "$@"
all_redirections+=" ${argv_q} " # append that to all_redirections string
}
to_all_redirections() { eval "tee ${all_redirections}" >/dev/null; }
...您可以运行:
add_redirection exe_1 arg_1_a arg_1_b
add_redirection exe_2 arg_2_a
# ...
add_redirection exe_n arg_n
...然后,当你有一个程序,其输出将被复制到这些可执行文件的输入中时:
yourprogram | to_all_redirections
4赞
Barmar
7/28/2023
#2
不要使用进程替换,而是在循环中创建一堆命名管道,然后运行每个进程,并将其 stdin 重定向到其中一个管道。然后用于写入所有管道。tee
progs=(exe_1 exe_2 ...)
args=(args_1 args2 ...)
pipes=()
arraylenth=${#progs[@]}
for (( i=0; i<${arraylength}; i++ ))
do
pipe=/tmp/pipe.$$.$i
mkfifo "$pipe" && pipes+=("$pipe") && "$progs[i]" "$args[i]" < "$pipe" &
done
tee "${pipes[@]" > /dev/null
# Clean up
rm -f "${pipes[@]"
此解决方案使每个程序都只使用 1 个参数运行。很难使它更通用,因为 bash 没有二维数组。
评论
0赞
Mega-X
7/28/2023
选择这个因为它避免了使用,感谢您的支持!:)eval
1赞
pjh
7/28/2023
#3
此 Shellcheck-clean 代码不使用,并且可以处理具有任意数量参数的任意数量的程序:eval
#! /bin/bash -p
prog_args=( ::: sed -e 's/^/[1] /'
::: sed -e 's/^/[2] /'
::: sed -e 's/^/[3] /' )
exec 3>&1
function tee_to_progs
{
(( $# < 2 )) && return 1
local -r startstr=$1
shift
local pargs=()
while [[ $# -gt 0 && $1 != "$startstr" ]]; do
pargs+=( "$1" )
shift
done
if (( $# == 0 )); then
# Last program to run. It can just read stdin and write stdout.
"${pargs[@]}"
else
tee >("${pargs[@]}" >&3) | tee_to_progs "$@"
fi
}
tee_to_progs "${prog_args[@]}"
- 该数组包含要运行的程序和参数。由于 Bash 不支持嵌套数组,因此每个命令前面都有一个标记字符串,以便识别单独的程序和参数。我使用了这个字符串(因为它在 GNU Parallel 中用于类似的目的),但任何不用作程序名称或参数的字符串都可以改用。该代码假定数组中的第一个字符串(无论它碰巧是什么)是标记字符串,因此如果字符串发生更改,则不需要更改代码。我测试了使用而不是.
prog_args
:::
_
:::
- 这些命令只是示例。我用它们进行测试,因为每个程序的输出都可以很容易地识别出来。
sed
- 请注意,此代码仅支持运行简单命令。如果您需要运行其他命令(例如使用重定向的命令),则需要使用不同的方法(可能涉及可怕的)。
eval
exec 3>&1
使文件描述符编号 3 与标准输出相关联。它在代码中用于确保输出进入“真实”标准输出。- 该函数运行其参数列表中的第一个程序,并将输入复制到该程序,并将输出通过管道传递给该函数的递归调用,该函数对第二个和后续程序执行相同的操作。
tee_to_progs
tee
tee
1赞
Ole Tange
8/7/2023
#4
GNU Parallel 为此提供了:--tee
cat input | parallel --tee --pipe my_program {} ::: arg1 arg2 arg3
在内部,它的工作方式与 Barmar 的解决方案非常相似,但您可以获得 GNU Parallel 的输出控制:
- 输出是序列化的,因此两个作业的输出不会混合
- 您可以保留订单
--keep-order
- 你可以每行
--tag
临时文件的清理是在作业完成之前完成的,因此如果脚本被终止,则无需清理临时文件。
例如
seq 10000 | parallel --tee --pipe --tag --keep-order grep {} ::: {1..9}
评论
eval
pee