通过 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
}
我尝试从并行中删除 {}。不太确定我做错了什么。
我发现调用中有两个问题,另外还有一些函数问题:
--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。
- Bash 在
parallel
甚至看到它之前就解释了您命令中的管道 |
。要 运行 管道,请引用整个命令:
parallel ... 'b=$(cat); _fix_col_number "$b" | _unify_null_value "$b"' >> ...
_fix_col_number
似乎假设它的参数是单行,但接收到 100MB 块。
_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"; }
.
但是,不要在这些小事上浪费时间。 sed
、awk
或 perl
中的完全重写很容易,并且应该优于现有函数的所有优化。
尝试
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
指定)。
让我向您展示我的 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
}
我尝试从并行中删除 {}。不太确定我做错了什么。
我发现调用中有两个问题,另外还有一些函数问题:
--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。- Bash 在
parallel
甚至看到它之前就解释了您命令中的管道|
。要 运行 管道,请引用整个命令:
parallel ... 'b=$(cat); _fix_col_number "$b" | _unify_null_value "$b"' >> ...
_fix_col_number
似乎假设它的参数是单行,但接收到 100MB 块。_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"; }
.
但是,不要在这些小事上浪费时间。 sed
、awk
或 perl
中的完全重写很容易,并且应该优于现有函数的所有优化。
尝试
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
指定)。