unix講習会 シェルスクリプト2作業ディレクトリ ~/unix15/sge $ cd ~/unix15/sge $ ls...
TRANSCRIPT
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/shfor sm in results/*.sam do echo ${sm}done script1.sh
$ chmod +x script1.sh
$ ./script1.sh
results/ecoli.1.samresults/ecoli.10.samresults/ecoli.11.samresults/ecoli.12.sam
$ emacs script1.sh
繰り返し処理:for文
• 文字列やファイルのリストに対し、順番にある決まった処理をする
• リストはスペース区切りの文字列挙、配列、数字など
• for 後の変数に集合が順番に1つづつ代入され、その後に決まった処理が行われる
• 全ての集合が代入され終わったらfor文も終了
for 変数名 in 文字列などの集合
do 処理
done
for 開始
繰り返しリスト
do 処理
リスト最後?
for 終了 done
Yes
No
for文に使うリストの例
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' else echo '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 False
if : 条件判断の演算子• 条件判断の [ ] は、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
•ファイル名は同じにしつつ、拡張子は「.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コマンドと組み合わせてファイル名を変数に記憶する
$ date 2015年 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} ecoli.9
• 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)
2)
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を除いてqsub script3.sh してみてください
演習 途中経過
script5.sh
#!/bin/sh
for sm in results/*.sam
do
fn=`basename ${sm} .sam`
echo “samtools view -bS ${sm} > results/${fn}.bam”
done
$ ./script3.sh
演習 解答
script5.sh
#!/bin/sh
#$ -cwd
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[@]} 配列全てを表す human mouse rat
$ fl=(results/*.sam)$ echo ${fl[0]}
$ fl=(`ls`)$ echo ${fl[@]}
カレントディレクトリのファイル名リストを配列に
results/ 内の.samで終わるファイル名リストを配列に
human� mouse� rat�
0,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,2�
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}
do
fn=`basename ${array[i-1]} .sam`
echo $fn ${array[i-1]}
done
バッククォート、配列をアレイジョブに応用
ファイル名に連続した数字が付いていなくてもアレイジョブにリストを与えることができる
配列の番号は0から始まるが、SGE_TASK_ID に入るのは1からなので 1を引いている ${list[${SGE_TASK_ID}-1]}
#!/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
script4.sh