我有多个管道,如下所示:
tee -a $logfilename.txt | jq string2object.jq >> $logfilename.json
或
tee -a $logfilename.txt | jq array2object.jq >> $logfilename.json
对于每个管道,我想申请多个命令。
每组命令类似于:
echo "start filelist:"
printf '%s\n' "$PWD"/*
或
echo "start wget:"
wget -nv http://web.site.com/downloads/2017/file_1.zip 2>&1
wget -nv http://web.site.com/downloads/2017/file_2.zip 2>&1
并且这些命令的输出都应该通过管道。
我过去尝试过的是将管道分别放在每个命令上:
echo "start filelist:" | tee -a $logfilename | jq -sRf array2object.jq >>$logfilename.json
printf '%s\n' "$PWD"/* | tee -a $logfilename | jq -sRf array2object.jq >>$logfilename.json
但在这种情况下,JSON脚本一次只能看到一行,因此它无法正常工作。
答案 0 :(得分:4)
以下内容可移植到POSIX sh:
#!/bin/sh
die() { rm -rf -- "$tempdir"; [ "$#" -gt 0 ] && echo "$*" >&2; exit 1; }
logfilename="whatever"
tempdir=$(mktemp -d "${TMPDIR:-/tmp}"/fifodir.XXXXXX) || exit
mkfifo "$tempdir/fifo" || die "mkfifo failed"
tee -a "$logfilename" <"$tempdir/fifo" \
| jq -sRf json_log_s2o.jq \
>>"$logfilename.json" & fifo_pid=$!
exec 3>"$tempdir/fifo" || die "could not open fifo for write"
echo "start filelist:" >&3
printf '%s\n' "$PWD"/* >&3
echo "start wget:" >&3
wget -nv http://web.site.com/downloads/2017/file_1.zip >&3 2>&1
wget -nv http://web.site.com/downloads/2017/file_2.zip >&3 2>&1
exec 3>&- # close the write end of the FIFO
wait "$fifo_pid" # and wait for the process to exit
rm -rf "$tempdir" # delete the temporary directory with the FIFO
使用bash,可以避免需要使用进程替换来管理FIFO:
#!/bin/bash
logfilename="whatever"
exec 3> >(tee -a "$logfilename" | jq -sRf json_log_s2o.jq >>"$logfilename.json")
echo "start filelist:" >&3
printf '%s\n' "$PWD/*" >&3
echo "start wget:" >&3
wget -nv http://web.site.com/downloads/2017/file_1.zip >&3 2>&1
wget -nv http://web.site.com/downloads/2017/file_2.zip >&3 2>&1
exec 3>&1
然而,当jq
失败或等待jq
在您的脚本之前完成写入时,会检测到不让您(没有bash 4.4)的事情退出。如果您希望在脚本退出之前确保jq
完成,那么您可以考虑使用flock
,如下所示:
writelogs() {
exec 4>"${1}.json"
flock -x 4
tee -a "$1" | jq -sRf json_log_s2o.jq >&4
}
exec 3> >(writelogs "$logfilename")
以后:
exec 3>&-
flock -s "$logfilename.json" -c :
由于jq
函数中的writelogs
进程会对输出文件产生锁定,因此最终flock -s
命令无法同时获取锁定输出文件,直到jq
退出。
在任何一个shell中,以下内容同样有效:
{
echo "start filelist:"
printf '%s\n' "$PWD"/*
echo "start wget:"
wget -nv http://web.site.com/downloads/2017/file_1.zip 2>&1
wget -nv http://web.site.com/downloads/2017/file_2.zip 2>&1
} >&3
可能,但不建议将代码块传输到管道中,从而完全取代FIFO使用或进程替换:
{
echo "start filelist:"
printf '%s\n' "$PWD"/*
echo "start wget:"
wget -nv http://web.site.com/downloads/2017/file_1.zip 2>&1
wget -nv http://web.site.com/downloads/2017/file_2.zip 2>&1
} | tee -a "$logfilename" | jq -sRf json_log_s2o.jq >>"${logfilename}.json"
......为什么不可取?因为在POSIX sh中无法保证管道的哪些组件(如果在同一个shell解释器中运行与脚本的其余部分相同);如果上面的不是在脚本的同一块中运行,那么变量将被丢弃(并且没有诸如pipefail
的扩展,退出状态也是如此)。有关详细信息,请参阅BashFAQ #24。
使用bash 4.4,进程替换在$!
中导出它们的PID,这些可以wait
。因此,您将获得另一种等待FIFO退出的方法:
exec 3> >(tee -a "$logfilename" | jq -sRf json_log_s2o.jq >>"$logfilename.json"); log_pid=$!
......然后,稍后:
wait "$log_pid"
作为前面给出的flock
方法的替代方案。显然,只有在你有bash 4.4的情况下才能这样做。