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 调用重定向 stderrstdout:

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