シェルスクリプトのシステム機能内で機能を実行するには?

シェルスクリプトのシステム機能内で機能を実行するには?

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は、他のツールを順番に呼び出すツールではなく、テキストを操作するためのツールです。これがシェルの目的です。テストするテキスト入力/出力を提供していないので、これはテストされていないと思いますが、コードで実行したいのは次のシェルループだけです(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()が呼び出されるたびにサブシェルを生成するため、awkを使用して利点を得るには遅すぎます。

バラよりawkスクリプトでシェル変数を使用する方法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"。この文字列が数値として扱われる場合(この例のように)0に等しいので、「0で除算」です。

あなたのコメントと更新された質問について:

質問でこの機能を変更してください。

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あなたに言うでしょう - シェルにもっと慣れるまで、常にスクリプトでこれを実行してください):

#!/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がサブシェルから関数を呼び出せるようにするには、関数をエクスポートする必要があります。使用したい出力ファイルが書き込み可能でない理由を自分で特定する必要がありますが、簡単です。

〜のようにスティーブン・チャジェラスコメントに記載されているように(現在実施されている上記の他のコメントに感謝します):

  • ファイルパスに文字が含まれていないという保証がない場合は、'任意のコマンドインジェクションの脆弱性と同じです。
  • これは\47ASCIIベースのシステムを想定しています(現在は比較的安全な仮定)。
  • chmod ug+rwこれをゼロに変更すると、これを防ぐことができますumask(しかし、ワールドファイルを書き込み可能にするのはとにかく悪い考えのように聞こえます)。

答え2

致命的:0で除算しようとしました

はい、引用の問題があるため、スラッシュは/目的の方法で文字列操作に含まれません。


これは不可解な引用を含むパイプラインデバッグのための非常に一般的な戦略です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

関連情報