GNU 并行排序的标准输出和标准错误
GNU parallel sorted stdout and stderr
我一直在使用 GNU parallel,我想保持输出顺序 (--kepp-order),按作业分组 (--grouped),但也有排序的 stdout 和 stderr。现在,分组选项首先打印 stdout,然后才打印 stderr。
举个例子,这两个命令会给出相同的输出吗?
seq 4 | parallel -j0 'sleep {}; echo -n start{}>&2; sleep {}; echo {}end'
seq 4 | parallel -j0 'sleep {}; echo -n start{} ; sleep {}; echo {}end'
谢谢,
如果您仍希望将 stderr 和 stdout 分开,则不能这样做。
原因是 stderr 和 stdout 使用缓冲输出缓冲到 2 个不同的文件。
但也许你可以解释一下你需要这个的目的。在那种情况下可能会有解决方案。
根据对其他答案的评论,要保持输出有序,只需将并行的 bash 调用重定向 stderr
到 stdout
:
parallel myfunc '2>&1'
例如,
parallel -j8 eval \{1} -w1 \{2} '2>&1' ::: "traceroute -a -f9" traceroute6 ::: ordns.he.net one.one.one.one google-public-dns-a.google.com
假设您不必使用 gnu parallel,主要要求是并行执行并保持 stderr 和 stdout 的有序输出;我们可以创建一个允许以下示例用法(加上提供 return 代码)的解决方案,您将在列表中获得执行结果,其中每个列表元素都在 return 列表中3 个字符串:索引为 0=stdout、1=stderr 和 2=return code.
source mapfork.sh
ArgsMap=("-Pn" "-p" "{}" "{}")
Args=("80" "google.com" "25" "tutanota.com" "80" "apa bepa")
declare -a Results=$(mapfork nmap "(${ArgsMap[*]@Q})" "(${Args[*]@Q})")
因此,为了打印第三个目的地 ("apa bepa") 的标准错误结果,您可以这样做:
declare -a res3="${Results[2]}"
declare -p res3
# declare -a res3=([0]=$'Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-21 18:55 CEST\nNmap done: 0 IP addresses (0 hosts up) scanned in 0.09 seconds' [1]=$'Failed to resolve "apa bepa".\nWARNING: No targets were specified, so 0 hosts scanned.' [2]="0")
printf '%b\n' "${res3[1]}"
mapfork.sh如下所示。它有点复杂,但它的部分已经在其他答案中解释过,所以我不会在这里提供细节:
Capture both stdout and stderr in Bash [duplicate]
#!/bin/bash
# reference:
nullWrap(){
local -i i; i=""
local myCommand=""
local -a myCommandArgs=""
local myfifo=""
local stderr
local stdout
local stdret
. <(\
{ stderr=$({ stdout=$(eval "$myCommand ${myCommandArgs[*]@Q}"); stdret=$?; } 2>&1 ;\
declare -p stdout >&2 ;\
declare -p stdret >&2) ;\
declare -p stderr;\
} 2>&1)
local -a Arr=("$stdout" "$stderr" "$stdret")
printf "${i}:%s\u0000" "(${Arr[*]@Q})" > "$myfifo"
}
mapfork(){
local command
command=""
local -a CommandArgs=""
local -a Args=""
local -a PipedArr
local -i i
local myfifo=$(mktemp /tmp/temp.XXXXXXXX)
rm "$myfifo"
mkfifo "$myfifo"
local -a placeHolders=()
for ((i=0;i<${#CommandArgs[@]};i++)); do
[[ "${CommandArgs[$i]}" =~ ^\{\}$ ]] && placeHolders+=("$i") ;done
for ((i=0;i<${#Args[@]};i+=0)); do
# if we have placeholders in CommandArgs we need to take args
# from Args to replace.
if [[ ${#placeHolders[@]} -gt 0 ]]; then
for ii in "${placeHolders[@]}"; do
CommandArgs["$ii"]="${Args[$i]}"
i+=1; done; fi
nullWrap "$i" "$command" "(${CommandArgs[*]@Q})" "$myfifo" &
done
for ((i=0;i<${#Args[@]};i+=$(("${#placeHolders[@]}")))) ; do
local res
res=$(read -d $'\u0000' -r temp <"$myfifo" && printf '%b' "$temp")
local -i resI
resI="${res%%:*}"
PipedArr[$resI]="${res#*:}"
done
# reference:
printf '%s' "(${PipedArr[*]@Q})"
}
我一直在使用 GNU parallel,我想保持输出顺序 (--kepp-order),按作业分组 (--grouped),但也有排序的 stdout 和 stderr。现在,分组选项首先打印 stdout,然后才打印 stderr。
举个例子,这两个命令会给出相同的输出吗?
seq 4 | parallel -j0 'sleep {}; echo -n start{}>&2; sleep {}; echo {}end'
seq 4 | parallel -j0 'sleep {}; echo -n start{} ; sleep {}; echo {}end'
谢谢,
如果您仍希望将 stderr 和 stdout 分开,则不能这样做。
原因是 stderr 和 stdout 使用缓冲输出缓冲到 2 个不同的文件。
但也许你可以解释一下你需要这个的目的。在那种情况下可能会有解决方案。
根据对其他答案的评论,要保持输出有序,只需将并行的 bash 调用重定向 stderr
到 stdout
:
parallel myfunc '2>&1'
例如,
parallel -j8 eval \{1} -w1 \{2} '2>&1' ::: "traceroute -a -f9" traceroute6 ::: ordns.he.net one.one.one.one google-public-dns-a.google.com
假设您不必使用 gnu parallel,主要要求是并行执行并保持 stderr 和 stdout 的有序输出;我们可以创建一个允许以下示例用法(加上提供 return 代码)的解决方案,您将在列表中获得执行结果,其中每个列表元素都在 return 列表中3 个字符串:索引为 0=stdout、1=stderr 和 2=return code.
source mapfork.sh
ArgsMap=("-Pn" "-p" "{}" "{}")
Args=("80" "google.com" "25" "tutanota.com" "80" "apa bepa")
declare -a Results=$(mapfork nmap "(${ArgsMap[*]@Q})" "(${Args[*]@Q})")
因此,为了打印第三个目的地 ("apa bepa") 的标准错误结果,您可以这样做:
declare -a res3="${Results[2]}"
declare -p res3
# declare -a res3=([0]=$'Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-21 18:55 CEST\nNmap done: 0 IP addresses (0 hosts up) scanned in 0.09 seconds' [1]=$'Failed to resolve "apa bepa".\nWARNING: No targets were specified, so 0 hosts scanned.' [2]="0")
printf '%b\n' "${res3[1]}"
mapfork.sh如下所示。它有点复杂,但它的部分已经在其他答案中解释过,所以我不会在这里提供细节:
Capture both stdout and stderr in Bash [duplicate]
#!/bin/bash
# reference:
nullWrap(){
local -i i; i=""
local myCommand=""
local -a myCommandArgs=""
local myfifo=""
local stderr
local stdout
local stdret
. <(\
{ stderr=$({ stdout=$(eval "$myCommand ${myCommandArgs[*]@Q}"); stdret=$?; } 2>&1 ;\
declare -p stdout >&2 ;\
declare -p stdret >&2) ;\
declare -p stderr;\
} 2>&1)
local -a Arr=("$stdout" "$stderr" "$stdret")
printf "${i}:%s\u0000" "(${Arr[*]@Q})" > "$myfifo"
}
mapfork(){
local command
command=""
local -a CommandArgs=""
local -a Args=""
local -a PipedArr
local -i i
local myfifo=$(mktemp /tmp/temp.XXXXXXXX)
rm "$myfifo"
mkfifo "$myfifo"
local -a placeHolders=()
for ((i=0;i<${#CommandArgs[@]};i++)); do
[[ "${CommandArgs[$i]}" =~ ^\{\}$ ]] && placeHolders+=("$i") ;done
for ((i=0;i<${#Args[@]};i+=0)); do
# if we have placeholders in CommandArgs we need to take args
# from Args to replace.
if [[ ${#placeHolders[@]} -gt 0 ]]; then
for ii in "${placeHolders[@]}"; do
CommandArgs["$ii"]="${Args[$i]}"
i+=1; done; fi
nullWrap "$i" "$command" "(${CommandArgs[*]@Q})" "$myfifo" &
done
for ((i=0;i<${#Args[@]};i+=$(("${#placeHolders[@]}")))) ; do
local res
res=$(read -d $'\u0000' -r temp <"$myfifo" && printf '%b' "$temp")
local -i resI
resI="${res%%:*}"
PipedArr[$resI]="${res#*:}"
done
# reference:
printf '%s' "(${PipedArr[*]@Q})"
}