MarkdownとBullet Journal

いわゆるプログラマーのつぶやき

【Linux】bash シェルスクリプト

bash シェルスクリプトコマンド一覧表(自分用)

スクリプト作成と実行

# test.sh実行はファイル保存の場所でいずれかのコマンドで実行
$ chmod 755 test.sh - $ ./test.sh
$ sh test.sh
$ bash test.sh

入力・出力

- echoで出力、 readで入力します。
test.sh
#!/bin/sh
read NAME
echo "Hello, $NAME!"

変数

  • 変数の名前として半角英数字とアンダーバーが使える
  • 変数に値を与える時=を前後空白なしで書く。文字列な場合"で囲む
  • 変数をアクセスする時は変数名の前に$を入れるか、${}で囲む
  • const変数はreadonlyを使う
  • 変数はunsetで削除できる
test.sh
#!/bin/sh
var="これは変数です"
var2="これも変数です"
echo "var2=$var2"
var2="var2が変更されました。"
echo ${var2}
readonly var
var="readonly varを変えてみる。"

特別な変数

$0  スクリプト名
$1 ~ $9 引数、1番目の引数を$1、2番目の引数を$2でアクセスする
$#  スクリプトに与えた引数の数
$*  *全部の引数をまとめて1つとして処理
$@  全部の引数を個別として処理
$?  直前実行したコマンドの終了値(0は成功、1は失敗)
$$  このシェルスクリプトのプロセスID
$!  最後に実行したバックグラウンドプロセスID

数値の置換

文法  説明
${var}        変数
${var:-word}  変数がまだセットされていないか空文字列の場合wordを返します。varに保存しません
${var:=word}  変数がまだセットされていないか空文字列の場合wordを返します。varに保存します
${var:?word}  変数がまだセットされていないか空文字列の場合置換に失敗し、スタンダードエラーにエラーを表示します
${var:+word}  変数がセットされている場合wordを返します。varに保存しません

test.sh
#!/bin/sh
echo "1 - ${var:-wordSetInEcho1}"
echo "2 - var = ${var}"
echo "3 - ${var:=wordSetInEcho3}"
echo "4 - var = ${var}"
unset var
echo "5 - ${var:+wordSetInEcho5}"
echo "6 - var = $var"
var="newVarValue"
echo "7 - ${var:+wordSetInEcho7}"
echo "8 - var = $var"
echo "9 - ${var:?StandardErrorMessage}"
echo "10 - var = ${var}"

実行結果:
1 - wordSetInEcho1
2 - var = 
3 - wordSetInEcho3
4 - var = wordSetInEcho3
5 - 
6 - var = 
7 - wordSetInEcho7
8 - var = newVarValue
9 - newVarValue
10 - var = newVarValue

配列 (Bash)

test.sh
#!/bin/bash
#bash shellで配列の書き方
ARRAY=(item1 item2 item3 item4)
ARRAY[0]="ITEM1"
ARRAY[2]="ITEM3"

echo "ARRAY[0]: ${ARRAY[0]}"
echo "ARRAY[1]: ${ARRAY[1]}"

#全てのアイテムをアクセスする
echo "ARRAY[*]: ${ARRAY[*]}"
echo "ARRAY[@]: ${ARRAY[@]}"

実行結果

$ ./test.sh
ARRAY[0]: ITEM1
ARRAY[1]: item2
ARRAY[*]: ITEM1 item2 ITEM3 item4
ARRAY[@]: ITEM1 item2 ITEM3 item4

オペレータ

  • shellでは算術演算子expr 数字 演算子 数字で計算できます。
演算子     意味       例
+          加         echo `expr 10 + 20` => 30
-          減         echo `expr 20 - 10` => 10
\*         乗         echo `expr 11 \* 11` => 121
/          割         echo `expr 10 / 2` => 5
%          剰余       echo `expr 10 % 4` => 2
=          指定       a=$b bの値はaに保存されます
==         同         [ "$a" == "$b" ] $aと$bが同じ場合TRUEを返します。
!=         異         [ "$a" != "$b" ] $aと$bが同じではない場合TRUEを返します。

比較       意味                例
-eq        イコール            [ "$a" -eq "$b" ] $aと$bが同じ場合TRUEを返します。
-ne        異なる              [ "$a" -ne "$b" ] $aと$bが違い場合TRUEを返します。
-gt        より大きい          [ "$a" -gt "$b" ] $aが $bより大きい場合TRUEを返します。
-lt        より小さい          [ "$a" -lt "$b" ] $aが $bより小さい場合TRUEを返します。
-ge        より大きいか同じか  [ "$a" -ge "$b" ] $aが $bより大きいか同じ場合TRUEを返します。
-le        より小さいか同じか  [ "$a" -le "$b" ] $aが $bより小さいか同じ場合TRUEを返します。
!          ではない            [ ! "$a" -gt "$b" ] $aが $bより大きくない場合TRUEを返します。
-o         どちらか            [ "$a" -gt "$b" -o "$a" -lt "$b" ] $aが $bより大きいか小さいかの場合TRUEを返します。
-a         両方                [ "$a" -gt 90 -a "$a" -lt 100 ] $aが 90より大きく100より小さい場合TRUEを返します。
-z         文字列が空か        [ -z "$a" ] $aが何も指定してない場合TRUEを返します
-n         文字列が空か        [ -n "$a" ] $aに何かを指定しした場合TRUEを返します
  • [ コマンドの引数に変数を指定するときは " でクォートする必要があります
  • 上記のオペレータを使ってif条件を書きます。

if 条件

ifの基本の書き方は if [ 条件 ] then コマンド fi です。

  • 条件が真の場合 then の次のコマンドを実行します。
  • 違う場合次々の elif [ 条件 ] を確認します。
  • 真の条件がない場合 else の次のコマンドを実行して終了します。
  • else がない場合は、そのまま終了します。
test.sh
#!/bin/sh

  if [ "$1" -gt "$2" ]
  then 
      echo "1番目の引数が2番目の引数より大きい"
  elif [ "$1" -eq "$2" ]
  then
      echo "1番目の引数と2番目の引数は同じです"
  else
      echo "1番目の引数が2番目の引数より小さい"
  fi

実行結果

$ ./test.sh 2 7
1番目の引数が2番目の引数より小さい
$ ./test.sh 10 5
1番目の引数が2番目の引数より大きい
$ ./test.sh 9 9
1番目の引数と2番目の引数は同じです

Switch 条件

  • switchの基本の書き方は case 変数 in 条件・値) コマンド ;; esac です。
  • 条件・値が変数と合う場合それの次のコマンドを実行します。
#!/bin/sh

DRINK="coffee"
case "$DRINK" in
    "beer") echo "ビールです" 
    ;;
    "juice") echo "ジュースです" 
    ;;
    "coffee") echo "プログラマーが飲むとコードに変化!" 
    ;;
esac

実行結果

$ ./test.sh
プログラマーが飲むとコードに変化!

ループ

  • breakキーワードで終了
  • continueキーワードで現在のループを飛ばすことができます。

while ループ

  • 条件が合うときループします。
test.sh
#!/bin/sh

a=0
while [ $a -lt 5 ]
do
    echo $a
    a=`expr $a + 1`
done

until ループ

  • whileの逆で、条件が合うまでループします。
test.sh
#!/bin/sh

a=0
until [ ! $a -lt 5 ]
do
    echo $a
    a=`expr $a + 1`
done

for ループ

  • forの基本の書き方は for 変数 in 複数値・変数・範囲 do コマンド done です。
  • 条件・値が変数と合う場合それの次のコマンドを実行します。
test.sh
#!/bin/sh

for var in 0 1 2 3 4  #範囲の書き方(Bash独自) => {0..4}
do
    echo $var
done
  • いずれのループも同じ結果:
$ ./test.sh
0
1
2
3
4

関数

test.sh
#!/bin/sh

#関数を指定します
MyFunction () { 
      echo "関数のechoです。"
}
MyParamFunc() {
      echo "引数1:$1 引数2:$2"
}

#関数を呼び出します
MyFunction
MyParamFunc param1 param2

実行結果:

$ ./test.sh
関数のechoです。
引数1:param1 引数2:param2

Linuxコマンドとの組み合わせ

  • シェルスクリプトの基本コマンドとunixコマンドを一緒に使えば効率的になります。
  • 以下のサンプルはファイル移動するコマンドmvとexprだけを使っています。
rename.sh
#!/bin/sh
#このスクリプトがあるディレクトリ中のtxtファイルを全部mytxt{番号}.txtに変更するスクリプト

index=1
for file in *.txt
do
    mv "$file" "mytxt${index}.txt"
    index=`expr $index + 1`
done

*実行結果:現在のディレクトリ中のファイルを確認後、名前を変更
$ ls
aaa.txt     bbb.txt     ccc.txt     ddd.txt     eee.txt     test.sh
$ sh rename.sh
$ ls
mytxt1.txt  mytxt2.txt  mytxt3.txt  mytxt4.txt  mytxt5.txt  test.sh

学習用サイト情報

shell操作

  • ファイルなどから?行目だけと指定した行の部分を出力する方法
  • 5行目を出力する例
$ sed -n 5p <file>
または
cat <file> | head -5 | tail -1

.vimrcを編集してC-dでDeleteを実装

  • どうしても欲しかった機能
inoremap <C-d> <Delete>

スクリプト作成

$ history | head -500 | tail -20 > aws.sh  // ターゲットコマンドのある辺りをファイル化
$ vim aws.sh                               // 編集で必要な1行のみ残す
$ sh aws.sh                                // シェルスクリプト実行
  • それをAliasにするとawsだけで起動する
  • もちろん最初からaliasにコマンド入力を記載してもよい
$ alias aws='sh aws.sh' //Shellスクリプトとの組み合わせ

readやechoの使い方

  • catやlessを使わずビルトインコマンドだけでファイル表示が可能
while read line; do echo $line; done < sample.c    // この場合は行頭のスペース・タブを削除
read -rd '' file < sample.c ; echo "$file"         // この場合はそのまま表示

正規表現

sed 文字列を置換

  • 例: sample.logファイル内のabcをdefに変換
sed s/abc/def/g sample.log

awk 文字列を抽出

  • 基本は$ awk 'パターン {アクション]'。
  • 列をフィールド、行をレコードと呼ぶ。
  • パターンを指定しない場合、全てのレコードに対してアクションが行われる。
  • 各フィールドの値はそれぞれ変数に格納される。
  • 第1フィールドは$1、第2フィールドは$2みたいに。 $0はレコード全体を示す。
  • 変数NFはフィールド数を、変数NRはレコード数を表す。
  • $NFで最後のフィールドの値を参照できる。
  • 区切り文字を-Fで指定(指定しないとスペース区切りになる)

  • 例: sample2.logファイル内で左から3番目のフィールド(文字列)を抽出

awk -F':' '{print $3}' sample2.log   // コロン区切りの場合
awk '{print $3}' sample3.log         // スペース区切りの場合

grep 文字列を検索

  • 例: 文字列abcが含まれる行を検索する
grep abc sample4.log

応用

  • パイプでつなげると、出力結果を渡すことができ、複数の処理をつなげることができる
  • 例: 文字列abcを含む行を検索後文字列abcをdefに変換、左から2番目と3番目の文字列を抽出してlessで表示
cat sample5.log | grep abc | sed s/aaa/bbb/g | awk '{print $2, $3}' | less

Readline viモード

  • bashをvi操作で使える
  • .bashrcに「set -o vi」の追記でOK

ディレクトリ移動