シェルスクリプトを書くときに見るメモ:


この記事はシェルスクリプトを作成する際、
他の言語と混同したり、忘れてしまいそうなことをまとめたリファレンスです。
筆者はシェルスクリプト初学者です。

シェルスクリプトの全体像

UTF-8に揃える、改行コードはLFを使う。

・文字化けしたら読みも書きもUTF-8に設定値を統一していく。
 ちなみに、windowsではSJIS(シフトJIS)になってしまうことが多いので注意する。

.vimrc
set encoding=utf-8
set fileencodings=utf-8,ucs-bom,iso-2022-jp-3,ucs-2le,ucs-2,cp932
lessコマンド
export LESSCHARSET=utf-8
less file.txt

・改行コードはすべてLF(正規表現: \n)にする。
 ちなみに、windowsでは CR+LF(正規表現: \r\n) になってしまうので注意する。

改行コード 改行コード 使用しているOS
LF \n UNIX
CR \r 古いmacOS
CRLF \r\n Windows

自由度の高いインデントと改行

・インデントはかなり自由。空白やタブは任意に入れることができる。
 ちなみにタブとスペースは別の文字コードを持つため留意する。
・改行はほぼ自由だが、コマンドの文末の改行はゆるい制約ある。
 なぜなら改行は"リスト"(コマンドを並べたもののこと)を終端する機能があり「;(セミコロン)」と同等の働きが若干あるから。
・見た目の改行を無視してコマンドの内容継続する場合は「(バックスラッシュ)」直後に改行を用いる。
 ちなみに、「|(パイプ)」直後の改行も前の行途中であるとシェルは判断する。

行の継続
# \(バックスラッシュ)直後の改行は行の継続を表す
$ l\
> s
# おかしな書き方ですが「ls」コマンドです。
行の継続
# |(パイプ)直後の改行は行の継続を表す。パイプラインとなる。
$ ls| 
> less

リダイレクトとパイプラインを使いこなすと強い。

入出力の切り替えのことをリダイレクトという。基本は下図。

リダイレクト
$ echo "Hello" > file.txt   #ファイルに新規書き込み
$ echo "World" >> file.txt  #ファイルに追記
$ file.txt > echo           #ファイルから実行

ファイル読み込みとファイルの上書きは同時にできない

例えば、あるファイルをソートして上書きしたいとき

ファイルの中身が消える例
$ uniq test.txt > test.txt

とすると、test.txtは空っぽで出力されてしまう。
「>(リダイレクト)」が付いていると処理の順番は

・リダイレクトで指定している出力先ファイルの作成
  ↓
・ファイルの読み込みと「uniq」などコマンドの実行
  ↓
・実行結果を作成したファイルに書き込み

と行われるため、空っぽのファイルを読み込んだこととなる。

シェルは
syeru循環.png

前のコマンド | 次のコマンド →パイプライン

・コマンド、パイプライン、リストの3種の違いを理解してコードを読み書きすると把握しやすい。
・シェルスクリプトは下図のようにコマンド→パイプライン→リスト→コマンド→…… と循環構造になっている。

シェルスクリプトのマナーや慣例

一行目に「#!/bin/sh」もしくは「#!/bin/bash」を必ず書く。

 つけないと余分な処理が発生する。(カーネルレベルでexecにいったん失敗し、補完機能として/bin/shが実行する)
 
<詳しい説明>
一行目の「#!hoge」は、スクリプト実行時にシステム内部で「hoge [このファイル]」というコマンドを実行してね、の意味。
つまり、#!/bin/python や #!/bin/perl、#!/bin/tail -lなどというshコマンド以外もできる。(引数は1個まで)
 
 #!/bin/tail -l
 「tail -l "ファイル"」コマンドは「"ファイル"の最後の行を表示しなさい」というコマンド。
 このスクリプトを実行すると「tail -l "このテキストファイル"」が実行され画面にはHello Worldと表示される。
 "Hello World"

bashを使うとわかっているのなら「#!/bin/bash」と単なるshではなくbashを明示すると良い。
なぜなら大半のLinuxディストリビューションは#!/bin/shがbashへのただのシンボリックリンクだから。
あとになってコードを読むするときに書き手のスタンスが伝わりやすいメリットもある。

あなたが使っているシェルは?

環境をチェックする コマンド
ログインシェルはなに? echo $SHELL
ログインシェルをbashに変更する chsh -s /bin/bash
pythonはある?どこ? which python
わたしはだれ? whoami
わたしはどこ? pwd

実はプライマリプロンプトでだいたい区別がつくシェルの種類
「$」:bash, ash,……Linuxや今どきなmac
「%」:tcsh, zsh,……FreeBSD(さくらレンタルサーバ)
「>」:……コマンド途中の改行(セカンダリプロンプト)
「#」:……rootユーザー

スクリプトを実行する方法

vimで編集中のファイルを実行するときは

vim
:w !sh

UNIXから
| |実行方法|#!bin/shの行|chmod +x|実行パス|実行シェル|引数の付け方|
|:----|:----|:----|:----|:----|:----|:----|
|コマンドとして|./file (実行パス上なら「./」は不要)|必要 (ないとカーネルexec失敗で補完実行)|必要|参照する|新しいシェル|file 引数1 引数2|
|シェルの引数として|sh file|不要|不要|参照しない|新しいシェル|file 引数1 引数2|
|標準入力として|sh < file|不要|不要|参照しない|新しいシェル|sh -s < file 引数1 引数2 (-sないとおかしな結果)|
|「.」ドットやsourceコマンド |. file (source file)|不要|不要|参照する|現在のシェル|file 引数1 引数2 (bash限定でできる)|

#!/bin/shが不要なもの多いがあっても問題ないので使おう。

実行権限を与えておく。

しないと(Permission Denied)エラーがでる。

#これきほん
touch test.sh      #つくったら
chmod +x test.sh   #実行権限与えておく
モード 数字
なし - 0
実行 x 1
書き w 2
読み r 4
読むのと実行ができる r-x 5
読み書きできる rw- 6
なんでもできる rwx 7

変数の宣言と参照

シェル変数という。

変数の代入と参照
var=99
TEISU=99

readonly TEISU=99
local var=100

・「=(イコール)」の両サイドに空白を入れない。((※算術演算のときは空白可))
・変数名は定数は大文字、変数は小文字にする
・定数は readonly をつけると誤った代入や unset から守れる。
・すべての変数は暗黙にグローバル変数となる。関数内の変数はlocalをつけることでスコープを制限できる。

関数内の変数はlocalをつける
var=99
func1() {
    local var=100
}
func
echo var

サブシェル「(小かっこ)」内なら変数を実質ローカルとして扱われる。これもOK。
※サブシェルは別のシェルを起動してコマンドが実行されるため。

サブシェルでローカル化してもOK
var=99
func() (
    var=100
)
func
echo var

コマンド実行結果の取得

コマンドの実行結果を変数に代入するなど、評価した出力をキャプチャする。
この手法をコマンド置換という。

var=$(echo hoge)
var=`echo hoge`

#ネスティングのわかりやすさに大きな差
var=`echo "\`echo hoge\`"`  #「`」」…ややこしめ。「\」でエスケープ、それを「""」で囲って単語分割やパス名展開を防ぐ。
var=$(echo $(echo hoge))    #「$()」…かんたん。可読性が高い。bashやFreeBSDなどで可能。

上の例のように方法が2通りある。
「$() (ダラー少かっこ)」で囲う。もしくは「` (バッククォート)」で囲う(キーボードの[shift + @]キー)。

算術式

((2重の小かっこ内))は算術式とみなされ、変数名の頭に$記号をつけずに参照できる
集中的に計算式を記述するのに便利。
・シェル上で特殊な意味を持つ記号がクォートなしで使えます。
・「=(イコール)」の両サイドに空白を入れられます。(算術式以外では空白不可)
・変数の中身が文字列のときは、その文字列を変数名として再度参照が行われます。(隠れたおもしろ仕様ですね)

算術式
echo "シェルスクリプトです"
((i=1))
while ((i <= 10))
do
  echo "$i"
  ((i++))
done

算術式のfor文はC言語ライクな書き方になります。

算術式のfor文
for ((i = 0; i <= 100; i++)){
  ((sum += i))
}
echo "$sum"
#もしエラーになら「:(ヌルコマンド)」で:((sum += i))を形だけコマンドの体にしておくと解決する。

算術演算の評価で使用できる演算子

演算子 説明
[変数]++
[変数]--
変数の値を評価したあとで変数に1を加え
変数の値を評価したあとで変数から1を引く
++ [変数]
--[変数]
変数に1を加えたあとで変数の値を評価する
変数から1を引いたあとで変数の値を評価する
-
+
負の数(2の補数)を表す
正の数を表す
!
~
論理的否定
ビットごとの否定(1の補数)
** 累乗
*
/
%
乗算
除算
剰余
+
-
加算
減算
<<
>>
左ビットシフト
右ビットシフト
<=
>=
<
>
以下なら真
以上なら真
より小さければ真
より大きければ真
==
!=
等しければ真
等しくなければ真
&
^
|
ビットごとのAND(論理積)
ビットごとのXOR(排他的論理和)
ビットごとのOR(論理積)
&&
||
論理的AND(論理積)
論理的OR(論理和)
[条件式] ? [式A] : [式B] 条件式が真なら式Aを、偽なら式Bを評価する
= 代入
*=
/=
%=
+=
-=
<<=
>>=
&=
^=
|=
乗算して代入
除算して代入
剰余をとって代入
加算して代入
減算して代入
左ビットシフトして代入
右ビットシフトして代入
ビットごとのAND(論理積をとって代入)
ビットごとのXOR(排他的論理和をとって代入)
ビットごとのOR(論理和をとって代入)
[式1], [式2] 式1、式2の順に評価し、式2の値を評価結果とする