GNU并行排序的stdout和stderr

时间:2015-10-12 15:25:01

标签: gnu-parallel

我一直在使用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'

感谢,

3 个答案:

答案 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})"
}