便携式方法
以下内容可移植到 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
避免 FIFO 管理(使用 Bash)
使用 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
等待退出(使用 Linux-y 工具)
然而,这个不允许让你做的事情(没有 bash 4.4)是检测jq 何时失败,或者在脚本退出之前等待jq 完成写入。如果您想确保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 :
因为writelogs 函数中的jq 进程在输出文件上持有锁,所以最终的flock -s 命令无法也在输出文件上获得锁,直到jq 退出。
旁白:避免所有 >&3 重定向
在任一 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
在 bash 4.4 中,进程替换将它们的 PID 导出到 $!,这些可以是 waited。因此,您可以获得另一种等待 FIFO 退出的方法:
exec 3> >(tee -a "$logfilename" | jq -sRf json_log_s2o.jq >>"$logfilename.json"); log_pid=$!
...然后,稍后:
wait "$log_pid"
作为前面给出的flock 方法的替代方案。显然,只有在您有 bash 4.4 可用时才这样做。