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 として保存•
実行権を与えてから実行してみる #!/bin/sh for sm in results/*.sam do echo ${sm} done script1.sh $ chmod +x script1.sh $ ./script1.sh results/ecoli.1.sam results/ecoli.10.sam results/ecoli.11.sam results/ecoli.12.sam $ emacs script1.sh繰り返し処理:
for文
•
文字列やファイルのリストに対し、順番にある決まった処理をする•
リストはスペース区切りの文字列挙、配列、数字など•
for 後の変数に集合が順番に1つづつ代入され、その後に決まった処 理が行われる•
全ての集合が代入され終わったらfor文も終了 for 変数名 in 文字列などの集合 do 処理 done for 開始 繰り返しリスト do 処理 リスト最後? for 終了 done Yes Nofor文に使うリストの例
for i in 1 2 3 4 5 6 7 do… done for i in {1..10} do… done for f in ./* do… done カレントディレクトリ内にある全てのファ イル名をワイルドカード「*」を使って リスト(変数 ${f} にファイル名が1つづつ 代入される) 1∼7までの整数をリスト(変数 ${i} に1∼ 7が順に代入される) 1∼10までの整数をリスト(変数 ${i} に 1∼10が順に代入される)if 文
•
script2.sh に実行権を与えてから実行してみる•
bowtieの結果ができているかの簡単なチェック $ chmod +x script2.sh $ ./script2.sh ok $ less script2.sh #!/bin/sh if [ -f results/ecoli.10.sam ] then echo 'ok' elseecho 'not ok' fi
•
[ ]
内の条件が真か偽か?で処理を変える•
if -> 条件 -> then•
「if」は必ず「fi」で終わらねばならない•
条件を複数設定したい場合は「elif」を使う if [ 条件 ] then 条件が真だった場合の処理 else 偽だった場合の処理 fi条件分岐:
if 文
if 開始 then 処理 if 終了 fi True False if 条件 else 処理•
複数の条件を設定:elif条件分岐:
if 文
if [ 条件1 ] then 処理 elif [ 条件2 ] then 処理 elif [ 条件3 ] then 処理 else 全て偽な場合の処理 fi if 開始 then 処理1 if 終了 fi True False if 条件1 else 処理 elif 条件2 then 処理2 True Falseif : 条件判断の演算子
•
条件判断の [ ] は、test コマンドの代替表現•
下記の演算子一覧は man test で見ることができる 数値比較 数1 -eq 数2 両辺が等しいと真 数1 -ne 数2 両辺が等しくないと真 数1 -gt 数2 数1 > 数2 の場合に真 数1 -lt 数2 数1 < 数2 の場合に真 数1 -ge 数2 数1 >= 数2 の場合に真 数1 -le 数2 数1 =< 数2 の場合に真 文字列比較 -n 文字列 文字列の長さが0でなければ真 ! 文字列 文字列の長さが0なら真 文字列1 = 文字列2 両文字列が同じなら真 文字列1 != 文字列2 両文字列が同じでなければ真 ファイルチェック -d ファイル名 ディレクトリなら真 -f ファイル名 通常ファイルなら真 -e ファイル名 ファイルが存在すれば真 -L ファイル名 シンボリックリンクなら真 -r ファイル名 読み取り可能ファイルなら真 -w ファイル名 書き込み可能ファイルなら真 -x ファイル名 実行可能ファイルなら真 -s ファイル名 サイズが0より大きければ真 ファイル名1 -nt ファイル名2 1が2より新しければ真 ファイル名1 -ot ファイル名2 1が2より古ければ真 論理結合 ! 条件 条件が偽であれば真 条件1 -a 条件2 条件1, 2 共真であれば真 条件1 -o 条件2 1, 2 どちらかが真であれば真複数のファイル処理
•
~/sge/results/ 内にある12個の .samファイルを .bamファイル
に変換したい
✓ samtools の sam -> bam 変換
samtools view -bS example.sam > example.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 コマンド:現在の日時を表示する•
basenameコマンドと組み合わせてファイル名を変数に記憶する $ date2015年 1月 20日 火曜日 11:26:31 JST
$ echo "Today is date"
Today is date
$ echo "Today is `date`"
Today is 2015年 1月 20日 火曜日 11:28:08 JST
挟んだコマンドを実行する
バッククォート
` `
$ fn=`basename result/ecoli.9.sam .sam`
$ echo ${fn}
•
sam -> bam 変換•
results/ecoli.1.sam を bam に変換し、result/ecoli.1.bam として保存1) 変数 fn に 「ecoli.1」を記憶しておき、
2) ${fn}.bam という名前でbamファイルを保存
basenameとバッククオートを使って拡張子を
変えた同名ファイルを作る
$ fn=`basename results/ecoli.1.sam .sam`
$ echo ${fn}
ecoli.1
$ samtools view -bS results/ecoli.1.sam > results/${fn}.bam
samtools view -bS example.sam > example.bam
1)
for文, basename , ` ` を使って取得したファイル名
を確認する
•
ワイルドカード「*」を使って、
results 内にある全ての .sam
ファイルをリスト
•
${sm} , ${fn} にはどんな文字が入るか?
$ less script3.sh for sm in results/*.sam do fn=`basename ${sm} .sam` echo ${sm} ${fn} done script3.sh $ 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を除いて
演習
途中経過
script5.sh #!/bin/sh for sm in results/*.sam do fn=`basename ${sm} .sam`echo “samtools view -bS ${sm} > results/${fn}.bam” done
演習
解答
script5.sh #!/bin/sh #$ -cwd for sm in results/*.sam do fn=`basename ${sm} .sam`samtools view -bS ${sm} > results/${fn}.bam done
•
名前または数値のリスト:「配列」を作り、記憶しておく•
配列名=(リスト…) で作成•
$ { 配列名 [ リスト番号 ] } で各リストの値を呼び出すシェルスクリプトにおける「配列」
$ array=("human" "mouse" "rat")
$ echo ${array}
human
$ echo ${array[2]}
rat
$ echo ${array[@]} 配列全てを表す
human mouse rat
$ fl=(results/*.sam) $ echo ${fl[0]} $ fl=(`ls`) $ echo ${fl[@]} カレントディレクトリのファイル名 リストを配列に results/ 内の.samで終わるファイル名 リストを配列に
human mouse rat
for文に配列を与え、番号で取り出す
•
ワイルドカード「*」を使って、results 内にある全ての .sam ファイ ルを配列 ${array} に記憶させる•
ファイルは12個あることがわかっているので、1∼12 のリストで内容 を取り出すが、配列の番号は0から始まるので 1 を引いている•
for i in {0..11} として 1 を引かなくても良い #!/bin/sh array=(results/*.sam) for i in {1..12} dofn=`basename ${array[i-1]} .sam` echo $fn ${array[i-1]}