簡単な繰り返し処理はもちろんのこと、大量なデータを処理する際もシェルスクリプトを書いて計算機に投入する必要があるので、シェルスクリプトは Linux ユーザーにとってなくてはならない存在である。bash はシェルスクリプトの一種で、ほとんどの Linux においてデフォルトのシェルとして使われている。
bash の変数
bash の変数には文字列あるいは配列を代入することができる。数字を代入しても、基本的に文字列として扱われる。
seq=ACAGTTAGCTAGCTGATGCACTAGTC
echo ${seq}
## ACAGTTAGCTAGCTGATGCACTAGTC
n=10
m=20
x=${n}+${m}
echo ${x}
## 10+20
arr=("AAA" "CCC" "GGG" "TTT")
echo ${arr}
## AAA
echo ${arr[@]}
## AAA CCC GGG TTT
bash では関数を定義することも可能で、その際に function
を利用して定義する。
function sum {
x=$1$2
echo ${x}
}
sum ACAG TTGT
## ACAGTTGT
bash 文字列
#
を利用することで、文字列の長さを求めることができる。
dna="TCCCA"
echo ${#dna}
## 5
文字列の切り出しは、開始位置と終了位置を指定して切り出す。
dna="TCCCA"
echo ${dna:0:3}
## TCC
文字列の置換はスラッシュを利用する。マッチしたパターンすべてに対して置換を行うときは、//
を利用し、マッチしたパターンの最初の 1 つだけに対して置換を行う場合は /
を利用する。
dna="TCTTA"
echo ${dna//T/U}
## UCUUA
echo ${dna/T/U}
## UCTTA
文字列の連結は 2 つの変数連続して書く。他のプログラミング言語のような +
や .
などの連結演算子は存在しない。
s1="ACCCC"
s2="GTTTT"
s="${s1}${s2}"
echo ${s}
## ACCCCGTTTT
配列の要素を連結して 1 つの文字列にする場合は、まず連結する際に区切り文字として利用される文字を IFS
で指定してから、[*]
で配列を展開して、新しい変数に代入すればよい。
arr=( "AAA" "TTT" "GGG" "CCC")
IFS=""
str="${arr[*]}"
echo ${str}
## AAATTTGGGCCC
1 つの文字列を、区切り文字で分割して配列にすることもできる。
str="SRR001,SRR002,SRR003"
IFS=","
ac=(${str})
echo ${ac[@]}
## SRR001 SRR002 SRR003
bash 配列
配列の作成
bash の配列を作るには、複数の要素を括弧で囲んで、変数に代入する。要素と要素の間はスペースで区切る。
arr=()
arr=("magnoliids" "monocots" "eudicots")
配列を作ってから、添え字を指定して要素の変更や追加を行うこともできる。
a=()
b=("mag" "mon" "eud")
a[0]="NN_CC320"
echo ${a[@]}
## NN_CC320
a=("${b[@]}" "NM_AC001" "NX_CB201")
echo ${a[@]}
## mag mon eud NM_AC001 NX_CB201
a[2]="NX_CB202"
echo ${a[@]}
## mag mon NX_CB202 NM_AC001 NX_CB201
配列要素の取り出し
要素を取り出すときは、添え字を指定して取り出す方法の他に、開始位置と終了位置を指定して複数の要素を取り出すこともできる。
arr=("FUN" "PLN" "PRT" "MAM" "HUM" "VRT")
echo ${arr[0]}
## FUN
echo ${arr[@]:1:3}
## PLN PRT MAM
echo ${arr[@]:2}
## PRT MAM HUM VRT
配列から最後の要素を取り出したい場合は、最後のインデックスを計算して取り出す。ただし、bash のバージョンが 4.2 以降の場合、-1
のようにマイナスでインデックスを指定することもできる。
${arr[${#arr[@]}-1]}
## VRT
echo ${arr[-1]}
## VRT
配列中すべての要素を順番に取り出す場合は for
を利用する。次のように、添え字を介して要素を取り出す方法と、要素自体を取り出す方法がある
arr=("FUN" "PLN" "PRT" "MAM" "HUM" "VRT")
for (( i = 0; i < ${#arr[@]}; ++i ))
do
echo ${arr[$i]}
done
for a in ${arr[@]}
do
echo ${a}
done
配列要素の置換
bash では、配列の要素に対して一括に置換などを行うことができる。ファイル名から拡張子を削除したり、あるいは拡張子を変更したりする場合に便利。
fnames=("NC_01.1.fasta" "NC_02.1.fasta" "NC_03.2.fasta")
次は、配列の各要素に対して、文字列 fasta を文字列 fa に置換する例である。
echo ${fnames[@]/fasta/fa}
## NC_01.1.fa NC_02.1.fa NC_03.2.fa
置換後の文字列を空文字にすると、文字列の削除が行える。次は、文字列 fasta を削除する例である。
echo ${fnames[@]/\.fasta/}
## NC_01.1 NC_02.1 NC_03.2
配列の各要素に対して、先頭から最初にマッチした位置まで取り除くとき #
を利用する。次は、文字列の先頭から最初のピリオドまでの部分文字列を取り除く例である。
echo ${fnames[@]#*\.}
##1.fasta 1.fasta 2.fasta
配列の各要素に対して、先頭から最後にマッチした位置まで取り除くとき ##
を利用する。次は、文字列の先頭から最後のピリオドまでの部分文字列を取り除く例である。
echo ${fnames[@]##*\.}
## fasta fasta fasta
次は、配列の各要素に対して、「文字列の後ろから最初にマッチした位置」から文字列の最後までの部分文字列を取り除く例である。
echo ${fnames[@]%\.*}
## NC_01.1 NC_02.1 NC_03.2
配列の各要素に対して、「文字列の後ろから最後にマッチした位置」から文字列の最後までの部分文字列を取り除く例である
echo ${fnames[@]%%\.*}
## NC_01 NC_02 NC_03
bash 関数
bash で関数を定義するときは function
を利用する。関数の内部で引数を参照するときに $1
、$2
、…のように参照する。
function sum {
let x=$1+$2
echo ${x}
}
sum 10 20
## 30
bash の関数に戻り値がないので、戻り値利用する場合はあらかじめグローバル変数を用意して、関数の中でそのグローバル変数に値を代入しておく必要がある。
y=""
function sum {
let x=$1+$2
y=${x}
}
sum 10 20
echo ${y}
## 30
bash 制御構文
if 構文
2 つの値を比較して真偽を判定してから、次の処理を行う if
構文は次のようにかける。bash の if
構文の書き方は、他のプログラミング言語のように自由度が高くないので、条件判断のところでスペースが多かったりまたは少なかったりするとエラーになる。
n=10
m=20
if [ ${n} -eq ${m} ]
then
echo "n = m"
elif [ ${n} -lt ${m} ]
then
echo "n < m"
else
echo "n > m"
fi
ファイル・ディレクトリの確認
ファイルあるいはディレクトリが存在するかどうかを確認して、作業を切り分ける例を示す。ファイルの存否を書くにインする場合は -e
または -f
を利用する。
fpath="/path/to/image.jpg"
if [ -e "${fpath}" ]; then
python predict.py "${fpath}"
else
echo "File not found."
fi
ディレクトリの存否を確認する場合は -d
を利用する。
dpath="/path/to/images"
if [ ! -d "${dpath}" ]; then
mkdir "${dpath}"
fi
for 構文
for
構文は配列の要素を 1 つずつ繰り返しながらとる出すループ構文である。
for i in 1 2 3 4 5
do
echo $i #1 2 3 4 5
done
for i in {1..5}
do
echo $i #1 2 3 4 5
done
for i in {1..10..2}
do
echo $i #1 3 5 7 9
done
for (( i=0; i<=5; i++ ))
do
echo $i #0 1 2 3 4 5
done
for i in {1..10}
do
if [ $i -gt 3 ]
then
echo $i #4
break
fi
done
while 構文
while
構文は、与えられた条件が真である限り繰り返すループ構文である。
declare -i i=0
while [ $i -lt 5 ]
do
echo $i #0 1 2 3 4
let i+=1
done
ほぼ同じような機能を有する until
構文もある。until
の場合は、ある条件に満たすまで繰り返すループ構文である。
declare -i i=9
until [ $i -lt 5 ]
do
echo $i #9 8 7 6 5
let i-=1
done
特殊変数と演算子
bash で利用される特殊変数と演算子。
特殊変数
$0 #シェルスクリプト名
$1 #第1引数
$2 #第2引数
$# #引数の個数
$@ #すべての引数を展開 "$1" "$2" ...
$* #すべての引数を展開 "$1 $2 ..."
$? #直前に実行したコマンドの終了ステータス
$! #直前に実行したコマンドのPID
$$ #シェルスクリプトのPID
算術演算子
N + M
N - M
N * M
N / M
N % M #剰余
論理演算子
N & M #AND
N | M #OR
N = M #==
N > M #>
N >= M #>=
N < M #<
N <= M #<=
N != M #!=
比較演算子
N -eq M
N -lt M
N -gt M
N -le M
N -ge M
サンプル
拡張子を変換
現在のディレクトリにある拡張子「fasta」で終わるファイルを「fa」に変更する例。
for i in `ls *.fasta`
do
mv ${i} ${i%.fasta}.fa
done
ファイル名の一括変換
ファイル名に「AAA」が含んでいれば、それを「BBB」に変更する例。
for i in `ls *AAA*`
do
mv ${i} `echo ${i} | sed 's/AAA/BBB/g'`
done
ファイルを一つにまとめる
複数の fastq ファイルを一つのファイルにまとめる例。
for i in `*.fastq`
do
cat ${i} >> all.fastq
done
パイプで変数を利用
シェルスクリプトでパイプを利用するとき、パイプ左側の内容を変数に代入し、パイプ右側で利用することができる。while read
を利用する。
echo "SRR000001 SRR000002 SRR000003" | while read line
do
IFS=" "
fqs=(${line})
for f in ${fqs[@]}
do
echo ${f}.fq
done
done
## SRR000001.fq
## SRR000002.fq
## SRR000003.fq