通过 GNU parallel 将参数传递给定义的 bash 函数

Passing args to defined bash functions through GNU parallel

让我向您展示我的 Bash 脚本的片段以及我如何尝试 运行 并行:

    parallel -a "$file" \
            -k \
            -j8 \
            --block 100M \
            --pipepart \
            --bar \
            --will-cite \
            _fix_col_number {} | _unify_null_value {} >> "$OUTPUT_DIR/$new_filename"

所以,我基本上是在尝试使用脚本中定义的 Bash 函数并行处理文件中的每一行。但是,我不确定如何将每一行传递给我定义的函数“_fix_col_number”和“_unify_null_value”。无论我做什么,都不会传递给函数。

我正在我的脚本中导出这样的函数:

declare -x NUM_OF_COLUMNS
export -f _fix_col_number
export -f _add_tabs
export -f _unify_null_value

提到的函数是:

_unify_null_value()
{
    _string=$(echo "" | perl -0777 -pe "s/(?<=\t)\.(?=\s)//g" | \
              perl -0777 -pe "s/(?<=\t)NA(?=\s)//g" | \
              perl -0777 -pe "s/(?<=\t)No Info(?=\s)//g")
    echo "$_string"
}
_add_tabs()
{
    _tabs=""

    for (( c=1; c<=; c++ ))
    do
        _tabs="$_tabs\t"
    done

    echo -e "$_tabs"
}
_fix_col_number()
{
    line_cols=$(echo "" | awk -F"\t" '{ print NF }')

    if [[ $line_cols -gt $NUM_OF_COLUMNS ]]; then
        new_line=$(echo "" | cut -f1-"$NUM_OF_COLUMNS")
        echo -e "$new_line\n"
    elif [[ $line_cols -lt $NUM_OF_COLUMNS ]]; then
        missing_columns=$(( NUM_OF_COLUMNS - line_cols ))
        new_line="${1//$'\n'/}$(_add_tabs $missing_columns)"
        echo -e "$new_line\n"
    else
        echo -e ""
    fi
}

我尝试从并行中删除 {}。不太确定我做错了什么。

我发现调用中有两个问题,另外还有一些函数问题:

  1. --pipepart 没有参数。从 -a file 读取的块通过标准输入传递给您的函数。尝试以下命令来确认这一点:
    seq 9 > file
    parallel -a file --pipepart echo
    parallel -a file --pipepart cat
    从理论上讲,您可以将标准输入读入一个变量并将该变量传递给您的函数,...
    parallel -a file --pipepart 'b=$(cat); someFunction "$b"'
    ...但我不推荐它,尤其是因为您的块每个都是 100MB。
  2. Bash 在 parallel 甚至看到它之前就解释了您命令中的管道 |。要 运行 管道,请引用整个命令:
    parallel ... 'b=$(cat); _fix_col_number "$b" | _unify_null_value "$b"' >> ...
  3. _fix_col_number 似乎假设它的参数是单行,但接收到 100MB 块。
  4. _unify_null_value 不读取标准输入,所以 _fix_col_number {} | _unify_null_value {} 等同于 _unify_null_value {}.

也就是说,您的功能可以得到显着改善。他们启动了很多进程,这对于较大的文件来说变得非常昂贵。您可以做一些微不足道的改进,例如将 perl ... | perl ... | perl ... 组合成一个 perl。同样,您可以直接处理标准输入,而不是将所有内容都存储在变量中:只需使用 f() { cmd1 | cmd2; } 而不是 f() { var=$(echo "" | cmd1); var=$(echo "$var" | cmd2); echo "$var"; }.
但是,不要在这些小事上浪费时间。 sedawkperl 中的完全重写很容易,并且应该优于现有函数的所有优化。

尝试

n="INSERT NUMBER OF COLUMNS HERE"
tabs=$(perl -e "print \"\t\" x $n")
perl -pe "s/\r?$/$tabs/; s/\t\K(\.|NA|No Info)(?=\s)//g;" file |
cut -f "1-$n"

如果您仍然觉得这太慢,请忽略 file;将命令打包成一个函数,导出该函数,然后调用 parallel -a file -k --pipepart nameOfTheFunction。选项 --block 不是必需的,因为 pipepart 将根据作业数量平均分割输入(可以用 -j 指定)。