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]}