Bashでシェルスクリプトを書くためのメモ

# bashについて学んだことをまとめてみた

## 記事の概要
- [入門bash](https://www.amazon.co.jp/%E5%85%A5%E9%96%80bash-%E7%AC%AC3%E7%89%88-Cameron-Newham/dp/4873112540)をよんで重要な部分や引っかかった部分をまとめたもの。
- その他自分で調べたことやshellscriptを書く中で覚えたテクニック等

## 対象読者
bashを使いたい人。
bashに興味がある人。

## 環境
Uubntu18.04

# 入門bash
- bashとは　ユーザからの入力を受け、それをオペレーションシステムに理解できる形に変換し、オペレーションシステムの出力をユーザに伝えるプログラムのこと。
- cd -　直前にいたディレクトリに戻る。
## ワイルドカード
- ?　任意の一文字
- ＊ 任意の文字列　
- [ab]　abを含むもの
- [!ab]　abを含まないもの
- [1-9]　1~9を含むもの
- [a-zA-Z]　小文字大文字いずれかを含むもの
ex: $ ls *[ab]*　abのいずれかを含むもの
## ブレース展開
- b{ed,olt,ar}s　beds,bolts,barsを表す
- b{ar{d,n,k},ed}s　bards,barns,barks,bedsを表す。
> ワイルドカードやブレース展開の特殊記号は正規表現だと意味が異なるのに注意
## 正規表現
> 正規表現はシングルクォートで囲む必要がある。正規表現に使われる文字をメタ文字といい、バックスラッシュエスケープが可能。
- .　任意の一文字に一致。
- []　[]内の中に含まれるいずれかの一文字とマッチする。ワイルドカードにあるように[1-4]のように指定可能。
- ^　行頭が^以下で始まるという意味。'^net'
- $　行末が$より前の文字列で終わる。'net$'
- \{m,n\}　m回以上n回以下の繰り返し
- \{m\}　ちょうどm回の繰り返し wine'\{2\}→winee
- \{m,\}　m回以上の繰り返し
- ()　正規表現をグループ化する。
## バックグラウンドジョブの優先度

```
$ nice commandのように実行すると優先度が低めで実行される。
$ renice command　コマンドの優先度の変更
```

## バックスラッシュエスケープ
- \が直前についた特殊記号をテキストとして扱う。
- また、\を末尾につけることでシェルコマンドを２行にわけることができる。
## コントロールキー
- Ctrl-c　コマンドを中止する。
- Ctrl-d　入力を終了する
- Ctrl-\　Ctrl-cが効かない場合に現在のコマンドを中止する。
- Ctrl-s　画面への出力を停止する
- Ctrl-q　画面への出力を再開する
- Ctrl-z　現在のコマンドを一時停止する。fg ジョブ番号、bg ジョブ番号
## コマンドラインでの編集
- set -o vi　デフォルトをvi
- set -o emacs　デフォルトをEmacsに
### emacsモード
- Ctrl-a　コマンドラインの先頭に移動
- Ctrl-e　コマンドラインの末尾に移動
- Ctrl-k　カーソルの位置の右側をすべて削除
- Ctrl-u　カーソルの位置の左側を削除する。
- Ctrl-j　エンター
- Ctrl-l　clearコマンドと同じ
- Ctrl-t　カーソルの位置の文字と左側の文字をいれかえる
### historyの展開 
- !!　最後のコマンドを参照
- !string　stringから始まるコマンドの参照
- !n　historyのn番目を参照
- !-n　hisotryの-n番目を参照
- ^string1^string2　最後に実行したコマンドの一部を置換して実行する。
### ワード指定記号
n　コマンドラインのn番目の単語を表す。ls -lと実行した後、!!:0 -lhS
$　最後の単語を表す。ls -lの後でmv !!a:$のように使う
x-y　x~yまでの単語を表す。
＊　0番目以外のすべての単語。1-$と同じ。
x＊　x-$と同じ
### 起動時に実行ファイル
- .bashrc
- .bash\_profile
- .bash\_logout
### シェルのオプション 
set -o オプション　
- emacs 
- igonoreeof　C-dを無効化
- noclobber　既存のファイルを上書きするリダイレクトの禁止
### クオーティング
- ''(単一引用符)　囲まれた部分の特殊記号を文字列として解釈する。
- ""(二重引用符)　囲まれた部分の特殊記号のうち、$のみ特殊記号として解釈する

```
$ word='apple'
$ echo "The $\word is \"$word\""
The $word is apple
```

### プロンプト変数
- 端末の行頭に表示する文字等を決める変数。PS1~PS4まである。
- PS1はプロイマリプロンプトと呼ばれ、デフォルト値は\s-\v\$である。ubuntu18.04ではいい感じに設定されているから変えなくて良さそう。
### コマンド検索パス
- ユーザーが使用したコマンドを検索するために使用され、PATHに保存されている。作成したシェルスクリプトのパスをPATH変数に追加するには、以下の方法が安全である。

```bash:bashrc
PATH=$PATH":/home/tomita/bashtest"
```

### ディレクトリのalias登録
- bashはエイリアスの値が空白で終わっていると、コマンドラインの次のワードに対してエイリアスを展開しようとする。そのため、ディレクトリ名だけをエイリアスとして登録したい場合には使用するコマンドを以下のように登録しておけば良い。以下はcdで例を示す。

```bash:bashrc
alias cd='cd'　cdの次の単語のエイリアスを探そうとする。
alias test='/home/tomita/model'
```
これにより、cd testでcd /home/tomita/modelが実行される。
### hash
- 使用されたコマンドをハッシュテーブルに保存することで検索速度を向上させている。hashをシェルで実行すると、使用した回数等が見られる。
### ディレクトリ検索パス
CDPATHはPATHと同様に:で区切られたリストからなる。CDPATHを設定することでcdで移動しようとしたディレクトリがカレントディレクトリで見つからなかった場合にCDPATHを引用する。

```bash:bashrc
CDPATH=:~/model
```
> この設定によって/model以下にあるディレクトリを検索することができる。
### 環境変数の設定
- OSが動いている時に使える変数で、exportコマンドによって設定する。

```
$ export コマンド=alice
```

### TERM変数
- 端末の操作に使用される。変数の値はterminfoデータベースにファイル名として登録されている小文字の短い文字列を使用する。
## シェルスクリプト
- シェルスクリプトにはいくつかの実行方法がある。
1. sourceコマンドを使う。
カレントシェルで現在のシェルを実行したときと同様にシェルスクリプトがカレントシェルで実行される。対象のシェルスクリプトを直接実行しないため、パーミッションがいらない。シェル環境における影響(alias)等を受ける。
2. スクリプト名を指定して実行する。
カレントディレクトリにシェルスクリプトが存在する場合、またはPATHにシェルスクリプトが存在するときに実行可能。実行するにはパーミッションが必要である。コマンドはサブシェルを立ち上げて実行するため、エイリアス等の影響を受けない。(逆にbashrc等で設定したエイリアスを使うためにはsourceコマンドで実行する必要がある。
3. スクリプト名に&をつけて実行
バックグラウンドで実行するというのはサブシェルの別表現である。よって&をつけた実行は端末のワークステーションが使用できる以外の差異はない。
### 関数について
関数が一度実行されていた場合にはシェルのメモリに格納されているため、素早く実行される。また、長いシェルスクリプトを簡潔にまとめることができる。
- 関数の定義

```
function 関数名
{
    シェルコマンド
}
```

または

```
関数名 ()
{
    シェルコマンド
}
```

- 関数に関連したコマンド

```
$ unset -f 関数名　関数の無効化
$ declare -f　ログインシェルに登録されている関数を調べる
```

- 関数を実行するにはシェルコマンドと同様に名前で実行することができる。しかし、関数の実行はサブシェルで実行されないため、シェル環境の影響を受ける。
### コマンドの優先度
コマンドを実行する際に同名が存在していた場合には以下の順で優先される。
1. エイリアス
2. functionやif,forなどのキーワード
3. 関数
4. cd,typeなどの組み込みコマンド
5. シェルスクリプトや実行可能ファイル
### typeコマンド
ある文字列のタイプを判定することができる。

```
$ type -a command　設定されているエイリアス、関数、シェルスクリプトをすべて表示する。
$ type -p command　コマンドを実行可能ファイル、シェルスクリプトに限って表示
$ type -t command　簡易表示
```

### シェル変数
- 環境変数は全て大文字とする風習がある。
- $をつけることで変数の値を取得する。
- 位置パラメータが存在し、それらは$1、$2のように表される。$0はスクリプト名を表す。
- ＊と＠はすべての位置パラメータを表すが、"をで囲った場合に差異が生じる。＊は一つの文字列として参照されるのに対して、@はn個の文字列の集合として参照される。
- ＃は変数の個数を表す。
- 関数に代入された変数をローカル変数と呼び、それは関数内部でのみ有効である。
- しかし、関数の内部で定義された変数と、$0に限っては関数の外部でも有効なグローバル変数である。
> 関数の内部で変数を再定義すると変更が適応される。また、位置パラメータも$0を除いて値が変更される。関数に代入した変数 (function ex1 ex2のように)は関数外部には持ち出せない。

```

function afunc
{
    echo in function: $0 $1 $2   ----3　$0は上書きされない
    var1= in function
    echo var1: $var1   ----4
}
var1="outside function"
echo var1: $var1   ----1
echo $0: $1 $2   ----2
afunc func1 func2 #関数の呼び出し
echo var1: $var1   ----5
echo $0: $1 $2   ----6
```

> test.bash arg1 arg2と実行すると

```
var1 : outside function   ----1
test: arg1 arg2   ----2
in function: test func1 func2    ----3
var1: in function    ----4　関数内部
var1: in function    ----5　関数内部で定義されたグローバル変数
test: arg1 arg2　----6 #ローカル変数は適応されず、もとの変数が出力される
```

- 関数内部にlocal文が含まれている場合には、その関数の変数はすべてローカル変数になる。

```
function afunc
{
    local var1
}
```

### 変数の構文について
- $varialeは${variable}を省略したもの
${varialbe}-1のように書きたい場合や、位置パラメータが10を超える場合には${10}のようにする必要がある。
### 文字列演算子の構文

```
${variable:-word}　variableが存在し、それがnullでない場合にはその値を返す。それ以外ならwordを返す。
変数が未定義の場合にはデフォルト値を返すのに用いる。

${variable:=word}　varableが存在し、それがnullでない場合にはその値を返す。それ以外であればvariableに**wordを設定**して返す。
変数が未定義の場合にデフォルト値を設定するのに用いる。

${variable:?messsage}　variableが存在し、尚且つnullでない場合に、その値を返す。それ以外であればvariableに続いてmessageを出力し、コマンドの出力を中止する。メッセージを省略すると、parameter null or not set)が出力される。

${variable:+word}　variableが存在し、尚且つnull出ない場合にwordを返す。それ以外であればnullを還す。
変数が存在しているかの評価に用いる。

${variable:offset:length}　部分文字列を展開する。$offsetの位置から、$lengthの長さの文字列を切り出す。lengthの値を省略すると、offsetの位置から末尾までの文字列を返す。offsetが負の場合は末尾からカウントする。
文字列の一部を返す(スライス)に使う。
```

### パターンとパターン照合

```
${variable#pattern}　variableの値の初めの部分とpatternが一致した場合、最も短く一致した部分を削除し、残りの部分を返す。
${variable##pattern}　variableの値の初めの部分とpatternが一致した場合、最も永く一致した部分を削除し、残りの部分を返す。
${variable%pattern}　variableの値の終わりの部分とpatternが一致した場合、最も短く一致した部分を削除し、残りの部分を返す。
${variable%%pattern}　variableの値の終わりの部分とpatternが一致した場合、最も長く一致下部分を削除し、残の部分を返す。
${variable/pattern/string}　variableの値でpatternと最も長く一致した部分をstringで置換する。この場合は最初に一致した部分だけが置換される。
${variable//pattern/string}　一致した部分が全て置換される。
```

> ここでいう長く一致したとは直の探索を行った結果、最後に一致した部分という意味である。逆に短く一致したとは最初に一致した部分という意味である。
ex

```
$path　　　　　/home/cam/book/long.file.name
${path#/＊/}         cam/book/long.file.name　/home/が一致した
${path##/＊/}                 long.file.name　/home/cam/book/が最も長く一致する。
%は後ろから探す。
${path%.＊}　　/home/cam/book/long.file       .nameが一致する。
${path%%.＊}   /home/cam/book/long            .file.nameが最も長い部分として一致する。
```

### 高度なパターン照合
- ＊(pattern-list)　指定されたパターンと一致するものを0個以上検出する
- +(pattern-list)　指定されたパターンと一致するものを1個以上検出する。null文字列とも一致する。
- ?(pattern-list)　指定されたパターンと一致するものを0個または1個検出する
- @(pattern-list)　指定されたパターンと一致するものを1個だけ検出する
- !(pattern-list)　指定されたパターンと一致しないものを検出する
> ＊(alice|hatter|hare)　alice,hatter,hareと一致するものを0個以上検出する。したがって、null文字列、alice,alicehatterなどと一致する。
> パターンにはシェルのワイルドカードも使用できる。

### コマンド置換
- 変数に値をコマンドライン引数にすること。

```
$(command)
   OR
`command`
```

### pushdとpopd
- ディレクトリスタックとは、新しいディレクトリへ一時的に移動し、もとのディレクトリをシェルに記憶させておくための仕組み。LIFO(Last-IN,First-Out)と呼ばれる構造がある。スタックに何かを積むことをプッシュ、スタックの先頭から取り出すことをポップするという。スタックはディレクトリを記録させるのが得意で、何度でも自分の場所を記録することができる。

## フロー制御
- フロー制御とは　変数の値や、コマンドが正常終了したかどうかといった条件に基づいて、プログラムの特定の部分だけを実行したり、特定の部分だけを繰り返し実行したりするための機能である。
### if/else

```
if 条件
then
    通常の処理
else
    エラー処理
fi
```
> [],testコマンドを使う際には、ifと同じ行にthenをおいてもいいがそれ以外の場合には改行が必要。

- 終了ステータス
すべてのコマンドは終了時に終了ステータスと呼ばれる、通常は0,異常終了時には1~255の数値を返す。ifは終了ステータスが0であれば真とみなす。
終了ステータスは、**$?**に保存される。

- 無限再起
組み込みコマンドを関数で上書きした場合には、関数の中でコマンドが呼び出されるため無限再起と呼ばれる関数の無限呼び出しに突入してしまう。
無限再起を防ぐにはbuiltinコマンドを使うことができる。

```
builtin 無限再起を防ぎたいコマンド
```

### return
- return N文が含まれている関数は、終了ステータスをNで終了する。return文がない関数では最後の文の終了ステータスが返される。
- returnが使用できる場合

1. 関数の内部
2. シェルスクリプトがsourceで実行できる場合
あるコマンドの終了ステータスが必要な場合には以下のようにする。

```
cd ()
{
    builtin cd "$@"
    es=$?
    echo "hogehoge"
    return $es
}
```

### 終了ステータスの組み合わせ
- &&と||
前の操作の終了ステータスが0かそれ以外(異常終了)かどうかで次の操作を実行する家どうか決まる。これをifと組み合わせるとand,orを表すことができる。
- &&=and

```
if statement1 && statement2
then

fi
```

statement1が通常終了し、2も通常終了した場合には、then句は最後に実行されたstatement2の終了ステータス0を返したとして実行される。
しかし、statement1,2のどちらかが異常終了した場合には最後に実行されたstatementの終了ステータスは0でないため、then句は実行されない。つまり、andと同様である。

- or

```
if statement1 || statement2
then

fi
```

statement1が異常終了した場合でも2が通常終了すればthen句は実行され、statement1が通常終了した場合には2は実行されない。つまり、どちらか片方が通常終了すれば良いため、orと同様であるといえる。

### 条件の評価
- [...]構文
[ condition ]が真かどうかで終了ステータスを返す。
#### 文字列の比較
- []内の条件式に使用される演算子はさまざまである。

```
str1 = str2　一致する時
str1 != str2　一致しない時
str1 > str2　大きい時
str1 < str2　小さい時
-n str1　str1がnullでないなら真(長さが0よりも大きい)
-z strq　str1がnullであるなら真(長さが0)
```

- exit文を用いた分岐
このコードが正常終了したかどうかを呼び出し元のプログラムに伝える。returnと同様に終了ステータスを指定できる。しかし、exitの場合はプロセスを終了させる働きもある。

```
exit 0　通常終了させる。
exit 1　異常終了する。
```

### testコマンド

```
test option = [ option ]
```

### ファイルの属性の確認

```
[ -a file ]　fileが存在する。
[ -d file ]　fileが存在し、ディレクトリである。
[ -e file ]　fileが存在する。(-aと同じ)
[ -f file ]　fileが存在し、かつ通常ファイルである(ディレクトリや特殊ファイルではない)
[ -r file ]　fileが存在し、読み取りのパーミッションが与えられている。
[ -s file ]　fileが存在し、空ではない。
[ -w file ]　fileに対して書き込みのパーミションがある。
[ -x file ]　fileに対して実行のパーミッションディレクトリなら検索のパーミッションがある。
[ -N file ]　fileが最後の読み取りの後に変更されている。
[ -O file ]　fileオーナである。
[ -G file ]　fileのグループIDが自分のものと一致する。
[ file1 -nt file2 ]　file1がfile2よりも変更時刻が新しい
[ file1 -ot file2 ]　file1がfile2よりも変更時刻が古い
```

ただし、ifで使う際には以下のようにかく。(;)が大事。

```
if [ condition1 ] && [ condition2 ]; then
    command
elif [ conditon1 ] || [ condition2 ]; then
    command
else
    :
fi
```

### 整数の条件評価
- シェルには数値を評価する機能もある。
- 文字列の<や>による比較では、文字列を数値としてではなく、辞書の順序(字句の大きさ)で比較する。文字列で比較すると、pがoxよりも大きいように、6は57よりも大きい。

```
-lt　より小さい
-le　以下
-eq　等しい
-ge　以上
-gt　より大きい
-ne　等しくない
```

### for文
- シェルのfor文は反復回数を指定できないのがCなどのfor文との大きな違い。反復回数はリストとして指定する。

```
for name [in list]
do
    $nameを使用するブロック文
done
```

in listの部分を省略すると、$@をとなる。

#### forのチートシート
- 配列をハードコーディングしてループ

```
items=(
    "altair"
    "betelgeuse"
    "canopus"
)

for item in "${items[@]}" ; do
    echo "[ ${item} ]"
done
```

- ハードコーディングその2

```
items[0]="altair"
items[1]="betelgeuse"
items[2]="canopus"

for item in "${items[@]}" ; do
    echo "[ ${item} ]"
done
```

- 連番の生成(ブレース展開)

```
for i in {1..10} ; do
    echo ${i}
done
```

- 連番の生成(seqコマンド)
- seq オプション [初期値[間隔]] 最終値

```
for i in $(seq 1 10) ; do
    echo ${i}
done
```

### case文
- case文では、ワイルドカードを使ったパターンと文字列の照合が可能である。if文を使うよりも簡潔に表現できる。

```
case 式 in
    パターン )
        文ブロック ;;
    パターン )
        文ブロック ;;
esac
```

```
for filename in "$@" ; do
    pnmfile=${filename%.*}.ppm  #filenameの.以下を削除
    case $filename in
        *.jpg ) exit 0 ;;  #filenameがパターンと一致していればexit
    esac
```

### select
- 簡単なメニューを作成することができる。

```
select name in list
do
    $nameを使用する処理
done
```

- サンプルプログラム

```bash:select.bash
select VAR in item1 item2 item3 item4 exit
#doからdoneまでループ処理
do
    echo "あなたが選んだ項目は$VARです。"
    #ループから抜けだすための分岐selectで使用したVARを使用する。「5」を選択すれば条件が成立する。
    if [ "$VAR" = "exit" ]; then
    break
    fi
done
```

> これを実行すると以下のように表示され、listの番号を入力するとvarにその中身が代入される。

```
1) item1
2) item2
3) item3
4) item4
5) exit
#? 1
```

- selectはcaseと併用したりすると面白い。

```
#!/bin/bash
select VAR in pwd ls exit
do
#繰り返しの中でcase分を使用している。
    case $VAR in
    "ls" ) ls -1 ;;
    "pwd" ) pwd ;;
    "exit" ) break ;;
    * ) echo "あなたの入力した番号は$REPLYです"
    esac
done
```

### whileとuntil

- 条件が真である限り実行するwhileと条件が偽である限り実行するuntile
- 大体の場合はwhileでuntilは書き換えられる。

```
while 条件
do
    コマンド
done
```

## コマンドラインオプション
- コマンドにオプションがついている場合は、オプションも位置パラメータに含まれる。
- 位置パラメータは読み取り専用で、代入は行えない。

```
command -option word1 word2
          $1     $2    $3
```

- shiftコマンド: 引数を指定した数だけシフトさせることができる。

## 型を持つ変数
- declareによって変数に型を与える。

## プロセス操作
- ジョブ制御

```
jobs ジョブ番号[1]と実行中のジョブを表示
jobs -l プロセスIDも表示
jobs -p プロセスIDのみ表示
jobs -s 停止しているジョブを表示
fg,bg 引数やジョブ名を指定してフォアグラウンドバックグラウンドにする。
C-z　ジョブの一時停止
```

> 現時点ではバックグラウンドからフォアグラウンドに切り替えるには一度ジョブを停止させる必要があると考えられる。そのため、シェルスクリプト内のみでジョブの切り替えは不可能であると考えられる。

### トラップ
- シグナルが受信されたときにコマンドが実行されるように設定できる。
- 既存のスクリプトの先頭に以下のように書く。

```
trap "echo 'You hit CTRL-C'" INT  CTRL-Cが受信されたらechoが実行される。
trap "echo 'You tride to kill me'" TERM  プロセスをkillしようとした場合にechoを実行する。
```

#### trapによるシグナルの無視
- HUPシグナル(モデムの使用中に接続が切断した場合に送信される)を受信した場合にはプロセスは強制終了される。
- 接続が切れた際にバックグラウンドプロセスが終了してほしくない!
- nohupコマンドがある。
- disown jobIDにも同じ機能がある。
### シェルスクリプトを並列化するには
- 実行したいコマンドをバックグラウンドにしたあと、waitコマンドをつかう。
- しかし、まだまだ課題がおおい。(2008年当時)

## シェルスクリプトのデバック機能
### set-oのオプション

```
bash -v test.bash コマンドを実行する前にエラーがあれば出力する。
noexec -n コマンドを実行しないで構文エラーを出力
xtrace -x コマンドを処理したあとにエラーを出力する。

```

# UNIX シェルスクリプト辞典
## 内部コマンドと外部コマンド
- 外部コマンド: コマンドが実行ファイルとしてシェルの外部にあるコマンド。つまり、パスがあるコマンドｈなすべて外部コマンドであるといえ、ほとんどのコマンドが外部コマンドに分類される。
- 内部コマンド: コマンドがシェル自身に組み込まれた(ビルトインコマンド)のこと。cd,pwdなどのディレクトリ操作系のコマンドが内部コマンドであることが多い。

## コマンド使用例
### compleate
- tabの補完方法を変更できる。

```
compleate -d rmdir  rmdirの補完候補として現れるものをディレクトリのみに変更
```

### typeset
- 変数に属性を設定する。

```
typeset 変数  　　変数の値を表示
typeset -p 変数   変数の値と属性を表示
typeset -a 変数   変数を配列変数にする
typeset -f 変数   変数を関数として設定
typeset -i 変数   変数を整数として設定
typeset -x 変数   環境変数としてエクスポートする

typeset -i a=1
```

### echo
- -Eオプションをつけるとエスケープ文字が解釈されない
- -nで最後の改行を無視する
- -eオプションをつけることで以下のオプションが使用可能になる。
> オプションと文字列は""で囲むこと。

```
echo -e "hi\a"      アラートを鳴らす
echo -e "hi\bello"  前の文字を一文字消す
echo -e "hi\e"      エスケープ文字(文字化けした)
echo -e "hi\f"      フォームフィード文字(垂直タブと一緒？)
echo -e "hi\n"      改行する
echo -e "hi\rwd"    hiをwdに置換する
echo -e "hi\t"      水平タブを入れる
echo -e "hi\v"      垂直タブをいれる
echo -e "hi\\"      バックスラッシュを書く
```

### eval
- コマンドの実行結果をさらにコマンドとして実行したり、コマンドが代入された変数をコマンドとして実行できる。

```
CMD="ls -f"
eval $CMD
```

### exec
- 現在のシェルを停止して、別のシェルを実行する。

```
exec -l bash bashを再起動したのと同じ 
exec -c bash 環境をクリアにしてコマンドを実行できる
```

### export

```
export          エクスポート一覧を表示
export -f 変数  関数としてエクスポート
export -n 変数  エクスポートリストから外す
```

### fc
- コマンドの履歴を変更、表示する。

```
fc -e vim 1997 2008          エディタを指定して起動。historyの1997から2008を表示
fc -er vim 1997 2008         逆順にする
fc -s ll=ls                  履歴からllが含まれる最新の実行結果を探し、lsに編集して実行する
```

### getopts
- オプションの文字列を解析するためのコマンド
- 単独よりもシェルスクリプトの中で実行されることが多い。

```
bash:opt.bash
#!/bin/bash
while getopts 'a:b:cd' opt "$@" a,bは引数をとるが、c,dは取らない
//a:b:cを指定するとcのみ引数を取らない設定になる
do
    echo $opt     #存在するオプションかどうか
    echo $OPTARG  #aの引数が格納される
    echo $OPTIND  #引数の位置が格納される
    echo "-----"
done
```

```
$./opt.bash -a 0 b n -c 1
a
0
3     #aの引数の位置は3番目
-----
b
n
5
-----
c
      #引数を取らない
2
-----
```

### jobs
> Macだとジョブ番号の前に%を付けなきゃいけないみたい

```
jobs -l (ジョブ番号)    プロセスIDつきで表示
jobs -p (ジョブ番号)    ジョブ番号が属するプロセスグループの親のプロセスIDだけを表示
jobs -n (ジョブ番号)    ステータスの変更があった
jobs -r (ジョブ番号)    実行中のもののみ
jobs -s (ジョブ番号)    停止中のものだけ
jobs -x                Macだと使えない
```

### let
- 算術式を展開する。

```
let c=1+2
echo $c
>3
```

### local
- ローカル変数を定義する。
- オプションはtypesetと同じ

### pushd,popd
- pushdでディレクトリスタックを追加し、popdでディレクトリスタックを削除する。
- dirsコマンドでディレクトリスタックは見ることができる。
- popdを使うとディレクトリスタックを削除し、一番先頭(dirsでみた一番左のpath)に移動する。

```
pushd +n <path> ディレクトリスタックを追加し、dirsから見て左端を0としたときのn番目を先頭にする。
pushd -n <path> 数え方が逆転する
popd  +n        左から見てn番目を削除
popd  -n        右から見てn番目を削除
```

### read

```
read -d , name  name入力の終わりを,に変更。他のデリミタ文字に帰ることもできる。(標準はenter)
read -n 3 name  nameを代入する際に3文字目までしか入力しない。
read -p   name  プロンプト文字(tomita@cfsz5-2l)などを表示しない。
read -r   name  バックスラッシュを特殊文字として使用しない
read -s   name  パスワードを入力する時などと同じで入力したものが表示されない。
read -e   name  入力の際にreadlineを使用する。これがあるとC-hなどが使える。
read -t 6 name  6秒以内に入力がないと次に進む
read -u filename ファイルから読み込む 
read -a   name  配列を指定できる。
$read -a array
>a i u e o
$echo ${array[1]}
>i
```

### readonly
- 指定した変数を読み込み専用にする。

```
readonly -a variable  変数は配列を持つ
readonly -f variable  変数は関数
readonly -p           読み込み専用のリストを表示
```
******


## shellscript tips
### リダイレクト

```
>   # 標準出力をリダイレクト(上書き)
>>  # 標準出力をリダイレクトし、末尾に追加する。
2>  # 標準エラー出力をリダイレクト
&>  # 標準エラー出力と標準出力をリダイレクト
2>&1# 同上
```

#### dev/nullの使い方
- dev/nullはゴミ箱だと考えて、いらないものを入力リダイレクトさせることで必要なものだけを出力させることができる。
- if文などでコマンドの返り値のみが必要な時などはエラーや出力を/dev/nullにリダイレクトして破棄するとよい。

```
> /dev/null 2     #標準エラーを破棄する。
> /dev/null 2>&1  #標準エラーも標準出力も破棄する。
&> /dev/null = > /dev/null 2&1
```

### 自作シェルスクリプトの返り値を代入する
- 自作シェルスクリプトでechoした値はコマンド置換で文字列として保存できる。


### 文書を読み込む
- readarrayはファイルの終端もしくは、Ctrl-Dが入力するされるまで配列に1行ずつ読み込まれていく。
#### headerなどを取り除く。

```
 if [[ ${ALINE:0:1} == '#' ]] ; then contine ;fi
```

### コマンドが存在しているか判定

```
if [[ ${CMD:0:3} == N/A ]]
```

### 自作スクリプトにオプションをつける。

```
while getopts 'c:irR' opt; do
    case "${opt}" in
        c)
            #option1
            ;;
        i)
            #option2
            ;;
        *) 任意のパターンにマッチ
            exit 2;;
    esac
done
shift $((OPTIND - 1)) #解析した数だけ引数の番号を繰り上げる。これをやることで後ろの引数を$1に固定することができる。
```

### デフォルト値の設定
- オプションがなくても最低限動くようにデフォルト値を代入するシェルスクリプトのほうがよい。

```
PATTERN=${1:-PDF document} #$1になにも設定されていないならPDF documentを設定する。
```

### コマンドの実行結果を保存する。

```
command | while read FN #コマンドから結果が帰ってくる限り、FNに結果を代入する。
do
    #処理
done
```

### []と[[]]
#### 違い1:ワード名とパス名展開がされない。
- [[]]では変数展開後にワード分割されない。'foo bar'が複数の引数として認識されることはない。

- [[]]パス名展開をしない。(*,?,[]などのパターンを文字列として認識する。)
- つまり、*,?等の文字を含んでいても""などはいらない。

#### 違い2:数式の比較のしやすさ
- [[]]は、数式の比較演算子では算術式展開される。

```
var=123
[ "$((var))" -eq 123 ]; echo $?
[[ var -eq 123 ]]; echo $?  #同じ意味だが上よりスッキリ!
#
echo $((2**10))
[[ 2**10 -eq 512+512 ]]; echo $?
[[ '2 ** 10' -eq '512 + 512' ]]; echo $? #式に空白を入れたい場合
```

#### 違い3: ==の動作
- []の場合は完全一致。[[]]の場合はパターンマッチである。
- 直、クオーティングされている場合を除く。

```
[ /foobar == /fooba[rz] ]; echo $?     #一致しない
[[ /foobar == /fooba[rz] ]]; echo $?   #一致する
[[ /foobar == '/fooba[rz]' ]]; echo $? #一致しない
```

#### 違い4:比較演算子の種類
[[]]には以下の比較演算子がある。

```
    [[ 文字列 =~ 正規表現 ]] #文字列が正規表現に一致すれば真。
    [[ 文字列1 < 文字列2 ]]  #現在のロケールの辞書順で文字列1が文字列2よりも前なら真。
    [[ 文字列1 > 文字列2 ]]  #現在のロケールの辞書順で文字列1が文字列2よりも後なら真。
```

#### 違い5:論理演算子が異なる
-a -oの代わりに &&,||を使う。


[参考文献](https://fumiyas.github.io/2013/12/15/test.sh-advent-calendar.html)

### ファイルのあるなしで条件分岐する

- ディレクトリがない場合

```
if   [ -z "$(ls $Local_Dir_Path)" ]; #dir is available
then
    unmount
elif [ -n "$ls $Local_Dir_Path" ]; #dir is not exist
then
    echo DIRECTORY IS EMPTY!↲
fi
```

- ディレクトリはあるが中身がない時

```
dir_status=$(ls $Local_Dir_Path | tr -d "")↲
    case "$OS" in↲
        "Linux")↲
            if [ "$dir_status" != "" ]; #remote dir is available
           then
```

### 終了ステータスの確認

```
command
echo $?
```