![如何在shell脚本中执行系统函数内部的函数?](https://linux22.com/image/225708/%E5%A6%82%E4%BD%95%E5%9C%A8shell%E8%84%9A%E6%9C%AC%E4%B8%AD%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%87%BD%E6%95%B0%E5%86%85%E9%83%A8%E7%9A%84%E5%87%BD%E6%95%B0%EF%BC%9F.png)
meta.txt
我有一个名为以下信息的文本文件:
还有一个名为data
output_folder = "data"
想要执行一个在函数conv
内部命名的函数,如下所示,它使用of中的路径system
查找并将文件转换为fastq.gz
2nd column
meta.txt
fastq.gz
fastq.txt
tail -n+2 meta.txt | awk -v output_folder=${output_folder} '{ system("convert " $2 $output_folder/"fastq.txt") }'
但我最终遇到了以下错误:
awk: cmd. line:1: (FILENAME=- FNR=1) fatal: division by zero attempted
答案1
awk 是一个操作文本的工具,而不是一个对其他工具进行排序调用的工具 - 这就是 shell 的用途。您没有提供要测试的文本输入/输出,所以这是一个未经测试的猜测,但我认为您需要做的就是您的代码显然试图做的就是这个 shell 循环(假设IFS
未设置或未修改):
while read -r _ file; do
convert "$file" "$output_folder/fastq.txt"
done < <(tail -n +2 meta.txt)
如果您出于某种原因确实想要使用 awk 进行调用,convert
那么它是:
output_folder="$output_folder" \
awk '
BEGIN { outfold = ENVIRON["output_folder"] }
NR>1{ system("convert \047" $2 ("\047 \047" outfold "/fastq.txt\047") }
' meta.txt
但这会在每次调用 system() 时生成一个子 shell,因此对于使用 awk 来说速度太慢而没有任何好处。
看如何在 awk 脚本中使用 shell 变量有关我如何使用ENVIRON[]
或替代方案的更多信息(如果您的 awk 变体不支持ENVIRON[]
.
至于为什么你会收到除以零的错误 - 你里面的代码system()
是:
"convert " $2 $output_folder/"fastq.txt"
其中output_folder
可能包含一些非数字字符串,因此如果您运行,gawk --lint
您会收到如下警告消息:
warning: attempt to field reference from non-numeric value
关于这一点,但是你有/
一个除法运算符,后跟文字字符串"fastq.txt"
,如果它被视为数字(如本例),则相当于零,因此“除以零”。
关于您的评论和更新的问题:
更改问题中的该函数:
function convert {
INPUT=$1
OUTPUT=$2
INPUT_R=0
if [ "${INPUT: -3}" == ".gz" ]; then
INPUT_S=1
fi
if [[ $INPUT_R -eq 1 ]]; then
gunzip -c ${INPUT} > ${OUTPUT}
else
cp -v ${INPUT} ${OUTPUT}
fi
chmod ug+rw ${OUTPUT}
}
解决它的问题(其中一些http://shellcheck.net会告诉你 - 总是在你的脚本上运行它,直到你更熟悉 shell):
#!/usr/bin/env bash
convert() {
local input=$1 output=$2
if [[ $input = *.gz ]]; then
gunzip -c -- "$input" > "$output"
else
cp -v -- "$input" "$output"
fi &&
chmod -- ug+rw "$output"
}
output_folder='/Users/doc'
# now include this:
while read -r _ file; do
convert "$file" "$output_folder/fastq.txt"
done < <(tail -n +2 meta.txt)
# or this at the end of the same script:
export -f convert # only works if sh is bash in your env since
# system() will call sh to run the command
output_folder="$output_folder" \
awk '
BEGIN { outfold = ENVIRON["output_folder"] }
NR>1{ system("convert \047" $2 ("\047 \047" outfold "/fastq.txt\047") }
' meta.txt
请注意,如果您希望 awk 能够在子 shell 中调用函数,则需要导出函数。您必须自己弄清楚为什么您尝试使用的输出文件不可写,但这应该很容易。
作为史蒂芬·查泽拉斯评论中指出(并感谢上面现已实施的其他评论):
- 如果不能保证文件路径不包含
'
字符,则相当于任意命令注入漏洞。 - 这
\47
假设了一个基于 ASCII 的系统(目前这是一个相对安全的假设)。 chmod ug+rw
可以通过将 更改为 0 来避免umask
(但无论如何,使世界可写文件听起来是一个非常糟糕的主意)。
答案2
致命:尝试除以零
是的,您遇到了引用问题,因此斜杠/
没有按照您希望的方式参与字符串操作。
这是调试管道的非常通用的策略awk
,包括引用废话。
不要创建一个字符串然后cmd
执行system(cmd)
,希望得到最好的结果,而是采用另一种方法。询问你的 awk 脚本输出该命令字符串发送到标准输出,然后管道的最后一部分是sh
(或bash
)。
这样做的优点是
- 您可以首先查看来自 的建议命令
awk
,以及然后一旦事情看起来不错,| sh
或者| sh -x
一旦事情看起来不错,并且 - 在调试可能有害的命令(例如 )期间
rm
,您首先看在你之前会发生什么跑步那些命令。
答案3
看起来你只需要:
(
umask 0
unset -v IFS
read -r discarded_header &&
IFS=' ' read -r discarded_first_field file &&
gzip -dcf < "$file" > data/fasq.txt
) < meta.txt
如果重点是制作文件的世界可读和可写副本(可能未压缩),该文件的路径位于meta.txt
.