シェルスクリプトの知識
ものぐさな人ほど楽をしようとする. 楽をするためには,どんな苦労も厭わない・かなり長時間を スクリプト作成にささげてしまう... 本末転倒ですね. そんなあなたは,研究者として生きていくなら少し注意が必要です.
あぁ,私の事ですね.はい. スクリプト作成・プログラミングは適当に切り上げて, さっさと研究しましょう » 同類の方々
sh (というか bash)のお話がメインです. 詳しい情報を知りたければ,man bash にて自分で調べてください.
参照: ちょっと便利なコマンド; スクリプト雛型 (hysk)
説明よりもサンプルが見たい人: 自分専用コマンド・関数
文字列
メタ文字
" $@&'()^|[]{};*?<>`\ スペース
メタ文字を表記する場合は,バックスラッシュを付ける.
エスケープシーケンス
エスケープ文字 | 意味 |
---|---|
\a | ベルを鳴らす |
\b | バックスペース |
\e | エスケープ文字 |
\f | フォームフィールド文字 |
\n | 改行文字 (line feed) |
\r | 復帰 (carriage return) |
\t | 水平タブ |
\v | 垂直タブ |
\数字 | 指定したASCII文字.数字部分は8進数3桁 |
\x数字 | 指定したASCII文字.数字部分は16進数3桁 |
- echo 文でエスケープ文字を使う場合は, -e を付ける
echo -e "hoge\t = hogehoge"
文字列操作
- 文字列の長さを調べる
${#変数名}
用例: echo ${#hoge} シェル変数 hoge の文字列長を表示
- 文字列入力
read を使う.
** sample.sh ** #!/bin/sh echo -n "What is your name?" read ans echo "Hello, $ans"
これを実行すると,キーボードからの入力待機になる.
read hoge1 hoge2
として,スペース区切りでキーボード入力すれば,2個の変数に代入される.
ファイルからの入力例は以下の通り:
** sample02.sh ******************************************************* BUFIFS=$IFS IFS= exec 3< 入力ファイル名 while read FL 0<&3 do 処理 done exec 3<&- IFS=$BUFIFS **********************************************************************
上記を実行すると,入力ファイルの内容を1行ずつ読み,変数FLに代入していく. 読み込んだファイル内容を利用する際は,$FL と指定する.
変数
シェル変数と環境変数
シェル変数と環境変数の違い
自分で調べろや,そんくらい.
予約済み環境変数 Reserved environmental variables
以下はシステムで予約済みの環境変数.変更するべからず.
PATH USER UID HOME HOSTNAME SHELL PWD OLDPWD LANG TERM PPID
変数定義
基本ルール
- ${変数名=値}で代入と変数の参照を同時にできる.
ただし,変数が未定義である必要がある.定義済みの場合,代入済みの値が使われる.
- ${変数名:=値}とすると,定義済みの場合であっても
代入済みの値が NULL の場合に限り,指定した新たな値が代入される.
- 変数を絶対に変えたくないときには,
readonly 変数名
と宣言すれば良い.解除は
unset 変数名
変数の一部を切り出す
変数の一部分を切り出す,つまり部分文字列の抜き出しの例
${hoge:n1:n2} とする.hoge が変数名,n1 は開始文字位置(最初の文字がゼロ),n2 は抜き出す文字数(開始文字含む). 例:日付(YYYYMMDD 形式)の変数から,年,月,日を分割 yyyymmdd=20120312 # 2012年3月12日 yyyy=${yyyymmdd:0:4} # "2012" を切り出し mm=${yyyymmdd:4:2} # "03" を切り出し dd=${yyyymmdd:6:2} # "12" を切り出し
変数の一部を置換 (bash内部コマンド)
変数の一部分を置換する事もできる. bash の内部コマンドを使うので,bash を使わないとできない.
${hoge/置換前文字列/置換後文字列} または ${hoge//置換前文字列/置換後文字列} とする. 前者は最初にマッチした文字列だけを置換,後者はマッチしたもの全てを置換. 私がよく使うのは,GMTによる作図で,EPS, PDF, PNG形式の画像ファイルの置き場所指定時. eps_path=$HOME/dv1/air_pollution/eps/timeseries pdf_path=${eps_path//\/eps\//\/pdf\/} png_path=${eps_path//\/eps\//\/png\/}
スラッシュをバックスラッシュでエスケープしてるので読み取りにくい. 同じことを sed を使っても可能 (See grep, sed, awk (hysk) > 入出力パスの一部のみ変更). 人それぞれの好み次第.
変数を配列で宣言する場合
#!/bin/bash declare -a foo bar #hoge=(0 1 2 3 4) foo=(foo0 foo1 foo2 foo3 foo4) bar=(bar0 bar1 bar2 bar3 hage1) for i in 0 1 2 3 4 ; do echo "$i ${foo[$i]} ${bar[$i]}" done # i
シェル変数の文字列の長さを知る
${#変数名}
※例1: 最も単純な例 i=hogehoge echo "Length of \$i = ${#i}" ※例2: (応用例) 時刻情報の設定,その文字列長を表示. 文字列が長いと,目で見て数えるのが面倒なので. ここでは固定長文字列を例にしたので利点を実感しにくいが, スクリプト中での可変長のシェル変数を使う際に,特に有効と思われる. (ISO 8601形式に準拠した,早崎が default で使う形式.25文字固定長) i=`date +"%FT%T%:z"` echo "Date/time = $i ; Length of \$i = ${#i}" (少し話が脱線: 上記で設定した変数i を使って,日付・時刻情報を抜き出す例) yyyy=${i:0:4} mm=${i:5:2} dd=${i:8:2} hour=${i:11:2} minute=${i:14:2} second=${i:17:2} tz_offset=${i:19:6} echo "$yyyy $mm $dd $hour $minute $second (offset = $tz_offset)"
時刻表記 (ISO 8601形式)については, 時刻に関する雑記参照.
特殊変数 (special variables)
return code の真 (true) と偽 (false)
基本は true = 0, false = 1
例題: ディレクトリ tmp, hoge が存在する(return code = 0) か 存在しない (return code = 1) かを確認
% \ls -l total 4 drwxrwxr-x 2 user1 user1 4096 Jun 24 12:59 tmp (ディレクトリ tmp だけが存在) % test -d tmp % echo $? 0 % test -d hoge % echo $? 1
特殊変数リスト
- $?
- result code of last command
- $$
- process ID
- $!
- process ID of current job (一つ前に動作させたジョブ)
- $-
- command option of current working shell
- $#
- number of argument(s)
- $*
- 全ての引数をスペースで区切りながら繋げて,そのまま引き渡す
- $@
- $* と同じ
- "$*"
- 全ての引数を1つの引数として扱って引きわたす
- "$@"
- "..." などでくくられた引数を1つの引数として扱って引きわたす
条件分岐
変数に関する条件分岐
test コマンドを使用. if 文と併用するときには,コードの読みやすさのために [ を使う事が多い. というか,自分の場合は [ で書くことが大部分で,test で表記することはほとんどない.
##### -z または -n : 変数がカラか否かのチェックに使える ### -z 文字列1 = 文字列1 の長さがゼロなら true を返す ### -n 文字列1 = 文字列1 の長さがゼロでないなら true を返す unset hoge hoge2 hoge=HOGE hoge2=HOGEHOGE if [ -z $hoge ] ; then echo "variable hoge is NULL" else echo "variable hoge have already used ; \$hoge = $hoge " fi
文字列の条件式
条件式 | 意味 |
---|---|
文字列, -n 文字列 | 指定した文字列が1文字以上あれば true |
-z 文字列 | 指定した文字列が0文字(何もない)状態なら true |
文字列1 = 文字列2 | 文字列1と文字列2が同じなら true |
文字列1 != 文字列2 | 文字列1と文字列2が異なれば true |
数値の条件式
指定できる数値は,正負の整数のみ.
条件式 | 意味 |
---|---|
数値1 -eq 数値2 | 数値1が数値2と同じであれば true |
数値1 -ne 数値2 | 数値1が数値2と等しくなければ(異なれば) true |
数値1 -lt 数値2 | 数値1が数値2より小さければ true |
数値1 -le 数値2 | 数値1が数値2以下であれば true |
数値1 -gt 数値2 | 数値1が数値2より大きければ true |
数値1 -ge 数値2 | 数値1が数値2以上であれば true |
ファイルに関する条件分岐
test コマンドによるファイル・ディレクトリの条件判別. 下記以外にもたくさんあるけれど, 私が頻繁に使うオプションは,せいぜい4つ( -d, -f, -h, -s). これ以外は,コンピュータ専門家やPC管理者くらいしか使わないような気がする.
条件式 | 意味 |
---|---|
-G | 指定ファイルが存在し,所属グループが実行ユーザのグループと同じなら true |
-O | 指定ファイルが存在し,ファイル所有者が実行ユーザと同じなら true |
-e | 指定ファイルが存在すれば true |
-d | 指定ディレクトリが存在すれば true |
-f | 指定ファイルが通常ファイルであれば true |
-h ないし -L | 指定ファイルがシンボリックリンクなら true |
-r | 指定ファイルが存在し,読み出し可能なら true |
-s | 指定ファイルが存在し,ファイルサイズが1以上なら true |
-w | 指定ファイルが存在し,書き込み可能なら true |
-x | 指定ファイルが存在し,実行可能なら true |
faile1 -nt file2 | file1 の修正時刻が file2 よりも新しければ true |
faile1 -ot file2 | file1 の修正時刻が file2 よりも古ければ true |
自分専用コマンド・関数
自分専用コマンド personal-use commands
自分がよく利用する手順,つまり一種のルーチンワーク的な作業は, 『自分専用コマンド』にしてしまえば,シェルプログラムを書くのも楽になる.
例えば私の場合,『ヘッダ行(先頭文字が "#" で始まる行)を削除する』だけのコマンド rm_header を用意し, それをパスの通ったディレクトリ($HOME/bin/ 以下)に置いている. ファイル実行権限を付与する事を忘れずに(chmod 755 rm_header).
#!/bin/sh # # Remove header line(s) which starts as sharp "#" symbol in a line. # # M. Hayasaki target=$1 ### remove header lines(s) and write to standard output! grep -e "^[^#]" ${target}
サンプルスクリプト・自作コマンド List of sample shell scripts
- euc2utf8: テキストファイルの文字コード変換(EUC から UTF8).ファイルの最終更新日時を変更しない.
- grib2_to_bin: GRIB2形式ファイルを direct access binary 形式に変換. (See also wgrib2 使用方法, GRIB & wgrib 情報)
- listup_files_period:
カレントディレクトリ以下の全ファイルから,
ある期間内で変更されたファイルを抽出,変更日時とファイル名を画面出力.
- (Default)引数なし: 直近20日以内
- 引数1つ(YYYYMMDD 形式): 指定日以後,今日まで
- 引数2つ(YYYYMMDD 形式): 第1指定日 - 第2指定日の期間
- mkctl: GRIB1 形式ファイルを第一引数として与え,GrADS ctl & idx ファイル作成. (See also wgrib 使用方法, GRIB & wgrib 情報)
- mkctl2: GRIB2 形式ファイルを第一引数として与え,GrADS ctl & idx ファイル作成. (See also wgrib2 使用方法, GRIB & wgrib 情報)
- mkdvipdf: TeX ファイルコンパイル,dvipdfmx コマンドを使ってPDF ファイルを作成. (See also TeX 個人的メモ (hysk))
- rm_header: ヘッダ行を削除,残り部分を標準出力. See also ヘッダ行の削除 (grep_sed_awk (hysk))
- reserve_header: ヘッダ行のみを標準出力
- re_archive_CFSR_var_month.sh: NCEP CFS再解析データ,要素ごとのファイルに再編集. See also NCEP CFSR 6-hourly の再アーカイブ, 再解析格子点気象データ情報; NCEP CFSR メモ (hysk)
- rearchive_MSM_pall.sh: 気象庁GPV MSMデータ,0-hr 予報値だけを切り出して1日あたり1ファイルに再編集. See also JMA GPV (MSM) 再アーカイブ, 現業気象データ情報
自分専用関数 personal-use functions
自分専用コマンドを作っていくと,用途は似ているが微妙に挙動を変更したコマンドを 多数用意したいことがある. そういったコマンドを一つずつ用意するのは面倒だし,メンテナンスが面倒. そんな場合は,シェルスクリプトではなく,関数を定義する. 関数ならば,一つのファイルに複数記述できるので,ファイル数がやたらと増えることもない.
私の場合,$HOME/bin/function/ 以下にファイルを設置. 必要に応じて,シェルプログラムの冒頭部分で読み込む( source $HOME/bin/function/hogehoge ). 読み込まない限り関数は使えないので,要注意. ログイン時に自動読み込みさせるのも一つの手段. ただし,現時点ではシェルプログラムごとに読み込む関数ファイルを明示するようにしている (2012-10-31 時点).
私の「デフォルトシェルスクリプト」の構造は, スクリプト雛形 (hysk) # シェルスクリプト を参照.
- chk_dir_file: ファイル・ディレクトリ操作についての関数.
私がシェルスクリプトを書くとき,ほぼ常時使用.下記に関数の一部を紹介する:
- chk_dir: ディレクトリが存在しなければ作成する
- chk_file: ファイルが存在していたら remove し,カラのファイルを作る
- get_dateinfo:
時刻処理についての関数.date コマンドを多用.
date コマンドの使いかたは,
日付・時刻処理(dateコマンド)や
時刻表記(ISO 8601形式)を参照.
下記に関数の一部を紹介する:
- utc2jst & jst2utc: 日本標準時と世界標準時の変換.引数に与えるのは10桁数値(yyyymmddHH)
- yyyymmdd2doy & doy2yyyymmdd: 年月日(8桁数値,20120415 なら2012年4月15日)から day of year (1月1日から起算した日数)に変換
- return_total_month: 開始年月と終了年月(共に yyyymm の6桁数値)を引数に与え,月の数をカウント,シェル変数 total_month に収納
- return_total_days: 開始年月日と終了年月日(共に yyyymmdd の8桁数値)を引数に与え,日数をカウント,シェル変数 total_day に収納
- obj_anal_function:
客観解析データについての関数.下記に関数の一部を紹介する:
- set_year: データ名を引数に与え,データの開始・終了年(シェル変数 s_yyyy, e_yyyy)を返す
- set_var_lev: 要素名・高度(気圧面)を引数に与え,それを連結したシェル変数 var_lev を返す. ただし,2次元要素・地表面要素の場合は高度の情報を付与しない(例: 500-hPa ジオポテンシャル高度の場合,HGT0500, SLP の場合,lev は1013 と設定している ので,SLP そのまま.SLP1013 などとしない.
(関連情報) 私が主に使用している客観解析気象データ(長期再解析; See also 再解析格子点気象データ情報)は, JRA25, JRA55, NCEP CFSR, ERA_Interim, である(初稿: 2013-05-07 時点; 上記リンク先は,早崎による私的メモページ).
更新履歴
- テキストファイルでのメモからWikiに転載.
更新日 | 内容 |
---|---|
2017-03-08 | 『変数の一部を置換』や『文字変数の文字列長を知る』などを追記. |
2016-02-28 | 『変数の一部を置換』や『文字変数の文字列長を知る』などを追記. |
2013-05-07 | 『自分専用コマンド・関数』の新規追加・リンク切れの修正.関連ページへのリンクを追加. |
2012-11-02 | 『サンプルスクリプト』を追加.これまで,いろんな場所に散在していたサンプルファイル情報をまとめるため. |
2012-10-31 | 『自分専用コマンド・関数』を追加. |
2012-03-24 | ファイルに関する条件式の記述を追記. 条件分岐の見出し構造を整理. |
2011-06-09 | 更新リストを時系列逆順に変更. return code 部分の記述ミスを修正.test の -d オプションが入ってなかった |
2008-07-03 | 特殊変数やメタ文字に関する記述を追記 |
2007-06-12 | 作成開始(記録にある日付では最古だが,もっと前から書いていたはず) |