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は、他のツールを順番に呼び出すツールではなく、テキストを操作するためのツールです。これがシェルの目的です。テストするテキスト入力/出力を提供していないので、これはテストされていないと思いますが、コードで実行したいのは次のシェルループだけです(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がサブシェルから関数を呼び出せるようにするには、関数をエクスポートする必要があります。使用したい出力ファイルが書き込み可能でない理由を自分で特定する必要がありますが、簡単です。
〜のようにスティーブン・チャジェラスコメントに記載されているように(現在実施されている上記の他のコメントに感謝します):
- ファイルパスに文字が含まれていないという保証がない場合は、
'
任意のコマンドインジェクションの脆弱性と同じです。 - これは
\47
ASCIIベースのシステムを想定しています(現在は比較的安全な仮定)。 chmod ug+rw
これをゼロに変更すると、これを防ぐことができますumask
(しかし、ワールドファイルを書き込み可能にするのはとにかく悪い考えのように聞こえます)。
答え2
致命的:0で除算しようとしました
はい、引用の問題があるため、スラッシュは/
目的の方法で文字列操作に含まれません。
これは不可解な引用を含むパイプラインデバッグのための非常に一般的な戦略です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
。