我一直在使用GNU并行,我希望保持输出顺序(--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'
感谢,
答案 0 :(得分:0)
如果您仍希望将stderr和stdout分开,则不能这样做。
原因是stderr和stdout使用缓冲输出缓冲到2个不同的文件。
但也许你可以解释一下你需要什么。在这种情况下,可能会有一个解决方案。
答案 1 :(得分:0)
根据对其他答案的评论,要使输出保持有序,只需将并行的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
答案 2 :(得分:0)
假设您不必使用gnu parallel,并且主要要求是并行执行,同时保持stderr和stdout的有序输出;我们可以创建一个允许使用以下示例的解决方案(加上提供返回代码),在该示例中,您将在列表中获得执行结果,其中每个列表元素都返回一个包含3个字符串的列表:索引为0 = stdout ,1 = stderr和2 =返回码。
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”)的stderr结果,您可以执行以下操作:
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]
How can I make an array of lists (or similar) in bash?
#!/bin/bash
# reference: https://stackoverflow.com/questions/13806626/capture-both-stdout-and-stderr-in-bash
nullWrap(){
local -i i; i="$1"
local myCommand="$2"
local -a myCommandArgs="$3"
local myfifo="$4"
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="$1"
local -a CommandArgs="$2"
local -a Args="$3"
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: https://stackoverflow.com/questions/41966140/how-can-i-make-an-array-of-lists-or-similar-in-bash
printf '%s' "(${PipedArr[*]@Q})"
}