Comments
Description
Transcript
UNIX講習会 シェルスクリプト2
UNIX講習会 シェルスクリプト2 31/July/2015 情報管理解析室 西出 浩世 シェルスクリプト応用編: 複数ファイルをまとめて処理する • 条件違いの同じフォーマットのデータが大量にあるとき • 一件づつコマンドを実行するのは大変 • • ヒューマンエラーの元にもなる SGEのアレイジョブ機能も使えるが、ごく簡単なコマンド には少々面倒 • 複数ファイルに対し繰り返し処理をしてくれるシェルスク リプトの書き方 作業ディレクトリ ~/unix15/sge $ cd ~/unix15/sge $ ls script* script2.sh script3.sh script4.sh ~/unix15/sge/results には、sam ファイルが12個入っていることを確認 $ ls results/*.sam 入っていない場合は以下のようにコピーしてください $ rm -r results $ cp -r /usr/local/data/unix15/sge/results . for 文 • エディタで新しく下記のスクリプトを記述し、script1.sh として保存 • 実行権を与えてから実行してみる $ emacs script1.sh #!/bin/sh for sm in results/*.sam do echo ${sm} done $ chmod +x script1.sh $ ./script1.sh results/ecoli.1.sam results/ecoli.10.sam results/ecoli.11.sam results/ecoli.12.sam script1.sh for 開始 繰り返し処理:for文 繰り返しリスト do 処理 for 変数名 in 文字列などの集合 do 処理 No done リスト最後? Yes for 終了 done • • 文字列やファイルのリストに対し、順番にある決まった処理をする • for 後の変数に集合が順番に1つづつ代入され、その後に決まった処 リストはスペース区切りの文字列挙、配列、数字など 理が行われる • 全ての集合が代入され終わったらfor文も終了 for文に使うリストの例 for f in ./* do… done カレントディレクトリ内にある全てのファ イル名をワイルドカード「*」を使って リスト(変数 ${f} にファイル名が1つづつ 代入される) for i in 1 2 3 4 5 6 7 do… done for i in {1..10} do… done 1∼7までの整数をリスト(変数 ${i} に1∼ 7が順に代入される) 1∼10までの整数をリスト(変数 ${i} に 1∼10が順に代入される) if 文 • script2.sh に実行権を与えてから実行してみる • bowtieの結果ができているかの簡単なチェック $ less script2.sh #!/bin/sh if [ -f results/ecoli.10.sam ] then echo 'ok' else echo 'not ok' fi $ chmod +x script2.sh $ ./script2.sh ok 条件分岐:if 文 • [ ] 内の条件が真か偽か?で処理を変える if 開始 False if [ 条件 ] if 条件 True then 条件が真だった場合の処理 then 処理 else 偽だった場合の処理 else 処理 fi • if -> 条件 -> then • 「if」は必ず「fi」で終わらねばならない • 条件を複数設定したい場合は「elif」を使う if 終了 fi if 開始 条件分岐:if 文 • 複数の条件を設定:elif if [ 条件1 ] then 処理 elif [ 条件2 ] False if 条件1 True then 処理1 elif 条件2 then 処理 True elif [ 条件3 ] then 処理2 then 処理 else 全て偽な場合の処理 fi else 処理 if 終了 fi False if : 条件判断の演算子 • • 条件判断の [ ] は、test コマンドの代替表現 下記の演算子一覧は man test で見ることができる 数値比較 文字列比較 数1 -eq 数2 両辺が等しいと真 数1 -ne 数2 両辺が等しくないと真 数1 -gt 数2 -n 文字列 文字列の長さが0でなければ真 数1 > 数2 の場合に真 ! 文字列 文字列の長さが0なら真 数1 -lt 数2 数1 < 数2 の場合に真 文字列1 = 文字列2 両文字列が同じなら真 数1 -ge 数2 数1 >= 数2 の場合に真 両文字列が同じでなければ真 数1 -le 数2 数1 =< 数2 の場合に真 文字列1 != 文字列2 ファイルチェック 論理結合 ! 条件 条件が偽であれば真 条件1 -a 条件2 条件1, 2 共真であれば真 条件1 -o 条件2 1, 2 どちらかが真であれば真 -d ファイル名 ディレクトリなら真 -f ファイル名 通常ファイルなら真 -e ファイル名 ファイルが存在すれば真 -L ファイル名 シンボリックリンクなら真 -r ファイル名 読み取り可能ファイルなら真 -w ファイル名 書き込み可能ファイルなら真 -x ファイル名 実行可能ファイルなら真 -s ファイル名 サイズが0より大きければ真 ファイル名1 -nt ファイル名2 1が2より新しければ真 ファイル名1 -ot ファイル名2 1が2より古ければ真 複数のファイル処理 • ~/sge/results/ 内にある12個の .samファイルを .bamファイル に変換したい ✓ samtools の sam -> bam 変換 samtools view -bS example.sam > example.bam • ファイル名は同じにしつつ、拡張子は「.bam」としたい ファイル名を取得するコマンド : basename • パスからファイル名だけを取り出して表示 $ basename ~/unix15/sge/results/ecoli.9.sam ecoli.9.sam • パスとファイル名の末尾から一致する文字列を削除して表示 $ basename ~/unix15/sge/results/ecoli.9.sam .sam ecoli.9 $ basename ~/unix15/sge/results/ecoli.10.sam 0.sam ecoli.1 挟んだコマンドを実行する バッククォート ` ` • date コマンド:現在の日時を表示する $ date 2015年 $ echo Today $ echo Today • 1月 20日 火曜日 11:26:31 JST "Today is date" is date "Today is `date`" is 2015年 1月 20日 火曜日 11:28:08 JST basenameコマンドと組み合わせてファイル名を変数に記憶する $ fn=`basename result/ecoli.9.sam .sam` $ echo ${fn} ecoli.9 basenameとバッククオートを使って拡張子を 変えた同名ファイルを作る • sam -> bam 変換 samtools view -bS example.sam > example.bam • results/ecoli.1.sam を bam に変換し、result/ecoli.1.bam として保存 $ fn=`basename results/ecoli.1.sam .sam` 1) $ echo ${fn} ecoli.1 $ samtools view -bS results/ecoli.1.sam > results/${fn}.bam 1) 変数 fn に 「ecoli.1」を記憶しておき、 2) ${fn}.bam という名前でbamファイルを保存 2) for文, basename , ` ` を使って取得したファイル名 を確認する $ less script3.sh for sm in results/*.sam do fn=`basename ${sm} .sam` echo ${sm} ${fn} done • ワイルドカード「*」を使って、results 内にある全ての .sam ファイルをリスト • script3.sh ${sm} , ${fn} にはどんな文字が入るか? $ chmod +x script3.sh $ ./script3.sh 演習 • for文を使って ~/unix15/sge/results/ 内の全 .samファイルを .bamに 変換する ✓ samtools の sam -> bam 変換 samtools view -bS example.sam > example.bam ✓ script3.sh を改変して作ること ✓ .bam ファイルも results/ 内に保存すること ✓ 結果のファイル名は、ecoli.1.bam ∼ ecoli.12.bam にすること ✓ basename と バッククォートを使いましょう ✓ qsub する前にsamtoolsコマンド全体を echo で出力してみましょう ✓ テストは ./script3.sh で実施し、echo が上手くいったらechoを除いて qsub script3.sh してみてください 演習 途中経過 #!/bin/sh script5.sh for sm in results/*.sam do fn=`basename ${sm} .sam` echo “samtools view -bS ${sm} > results/${fn}.bam” done $ ./script3.sh 演習 解答 #!/bin/sh #$ -cwd script5.sh for sm in results/*.sam do fn=`basename ${sm} .sam` samtools view -bS ${sm} > results/${fn}.bam done $ qsub script3.sh シェルスクリプトにおける「配列」 • • 名前または数値のリスト:「配列」を作り、記憶しておく • $ { 配列名 [ リスト番号 ] } で各リストの値を呼び出す 配列名=(リスト…) で作成 $ array=("human" "mouse" "rat") $ echo ${array} human $ echo ${array[2]} rat $ echo ${array[@]} 配列全てを表す 0,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,2 human mouse rat human mouse rat $ fl=(results/*.sam) $ echo ${fl[0]} results/ 内の.samで終わるファイル名 $ fl=(`ls`) $ echo ${fl[@]} カレントディレクトリのファイル名 リストを配列に リストを配列に for文に配列を与え、番号で取り出す #!/bin/sh array=(results/*.sam) for i in {1..12} do fn=`basename ${array[i-1]} .sam` echo $fn ${array[i-1]} done • ワイルドカード「*」を使って、results 内にある全ての .sam ファイ ルを配列 ${array} に記憶させる • ファイルは12個あることがわかっているので、1∼12 のリストで内容 を取り出すが、配列の番号は0から始まるので 1 を引いている • for i in {0..11} として 1 を引かなくても良い バッククォート、配列をアレイジョブに応用 ファイル名に連続した数字が付いていなくてもアレイジョブに リストを与えることができる script4.sh #!/bin/sh #$ -t 1-12 #$ -cwd list=(`ls ../rnaseq/test_fastq/`) bowtie2 -x ../rnaseq/ecoli_genome -U ../rnaseq/test_fastq/${list[${SGE_TASK_ID}-1]} -S results/ecoli.${SGE_TASK_ID}.sam 配列の番号は0から始まるが、SGE_TASK_ID に入るのは1から なので 1を引いている ${list[${SGE_TASK_ID}-1]}