如何在shell脚本中执行系统函数内部的函数?

如何在shell脚本中执行系统函数内部的函数?

meta.txt我有一个名为以下信息的文本文件:

在此输入图像描述

还有一个名为data

output_folder = "data"

想要执行一个在函数conv内部命名的函数,如下所示,它使用of中的路径system查找并将文件转换为fastq.gz2nd columnmeta.txtfastq.gzfastq.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)。

这样做的优点是

  1. 您可以首先查看来自 的建议命令awk,以及然后一旦事情看起来不错,| sh或者| sh -x一旦事情看起来不错,并且
  2. 在调试可能有害的命令(例如 )期间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.

相关内容