Help us understand the problem. What is going on with this article?

Bashシェルスクリプトの書き方(基本)のおさらいメモ

More than 1 year has passed since last update.

概要

タイトルの通りBashシェルスクリプトの書き方のおさらいメモです。
動作確認にDocker Desktop for Windowsとubuntu official imageを利用しました。

環境

  • Windows 10 Professional
  • Docker Desktop for Windows 2.0.0.3 (31259)
    • Ubuntu 18.04.2

参考

バージョン

ubuntuのバージョン

# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

bashのバージョン

# echo $BASH_VERSION
4.4.19(1)-release

シェル構文 (Shell Syntax)

クォート (Quoting)

シングルクォート (Single Quotes)

シングルクォート '...' で囲まれた文字は、パラメータ展開やコマンド置換は行われません。

$ message="World"
$ echo '$Hello ${message}!'
Hello ${message}!
$ echo '$(date "+%Y/%m/%d")'
$(date "+%Y/%m/%d")

シングルクォートで囲まれた文字にシングルクォートを含めることはできません。後述のエスケープキャラクタでエスケープすることもできません。

NG
$ echo 'in 80's'

ダブルクォート (Double Quotes)

ダブルクォート "..." で囲まれた文字は、パラメータ展開やコマンド置換が行われます。
パラメータ展開やコマンド置換をしたくない場合は後述のエスケープキャラクタを使います。

$ message="World"
$ echo "Hello ${message}!"
Hello World
$ echo "$(date "+%Y/%m/%d")"
2019/04/28

Escape Character

バックスラッシュ \ に続く1文字を文字の通り解釈します。

$ echo "Hello \${message}!"
Hello ${message}!
$ echo "\$(date "+%Y/%m/%d")"
$(date "+%Y/%m/%d")

シェルコマンド (Shell Commands)

ループ (Looping Constructs)

whileループ

whileはtest-commandの終了ステータスが1(為)である限りconsequent-commandsを繰り返し実行します。

syntax
while test-commands; do consequent-commands; done

i=0; while [ $i -lt 5 ]; do echo $i; ((i++)); done
i=0
while [ $i -lt 5 ]
do
  echo $i
  ((i++))
done

条件式の評価の部分 [ $i -lt 5 ]は、test [ $i -lt 5 ]test $i -lt 5 という書き方もあります。

デモ) ファイルを読み込む

その1) 半角スペース区切り

inpt.txt
1 2
2 3
3 4
4 5
5 6
while read c1 c2
do
  echo "$c1 + $c2 = $((c1 + c2))"
done < input.txt

その2) カンマ区切り

inpt.txt
1, 2
2, 3
3, 4
4, 5
5, 6

IFSで区切り文字を指定します。IFSはシェル変数で文字列を分割する区切り文字が設定されています。デフォルトは半角スペース、タブ、改行になります。

while IFS=, read c1 c2
do
  echo "$c1 + $c2 = $((c1 + c2))"
done < input.txt

forループ

wordsを展開しそれぞれの値に対してcommandsを実行します。値はnameにバインドされます。

syntax
for name [ [in [words …] ] ; ] do commands; done

for i in 0 1 2 3 4; do echo $i; done
for i in 0 1 2 3 4
do
  echo $i
done

forループには次のような算術式を使った書き方もあります。

syntax
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done

for (( i=0; i<5; i++ )); do echo $i; done
for (( i=0; i<5; i++ ))
do
  echo $i
done

デモ) ディレクトリの一覧

$ for dir in `find $(pwd) -mindepth 1 -type d`; do echo $dir; done

条件 (Conditional Constructs)

if

test-commandsの終了ステータスが0(真)の場合、consequent-commandsが実行されます。1(為)でelifが後続する場合はmore-test-commandsが実行され終了ステータスを判定します。

syntax
if test-commands; then
  consequent-commands;
[elif more-test-commands; then
  more-consequents;]
[else alternate-consequents;]
fi

例)

testコマンドを使う書き方。

$ if test $var = "A"; then echo "var is A"; else echo "var isn't A"; fi

testはコマンドなので単体で使用できます。

$ a="ABC"
$ b="ABC"
$ c="abc"
$ test $a = $b; echo $?
0
$ test $a = $c; echo $?
1

testコマンドの別名である[ ... ]を使う書き方もあります。

$ if [ $var = "A" ]; then echo "var is A"; else echo "var isn't A"; fi

デモ)ファイル・ディレクトリの判定

ファイル・ディレクトリの存在チェック

if [ -e input.txt ]; then echo "file is exists"; else echo "file is not exists"; fi
  • ファイルの存在チェックは-f
  • ディレクトリの存在チェックは-d
  • シンボリックリンクの存在チェックは-L

ディレクトリのシンボリックリンク

if [ -d dir1 -a -L dir1 ]; then echo "dir is symbolic link"; else echo "dir is not symbolic link"; fi

case

wordに一致する最初のパターンのcommand-listを実行します。同じcommand-listを実行するパターンが複数ある場合は|で区切って指定します。

syntax
case word in
    [ [(] pattern [| pattern]…) command-list ;;]…
esac

例)

var=9
case $var in
    [0-9])
      echo "var is numeric"
      ;;
    [a-z])
      echo "var is lower case"
      ;;
    [A-Z])
      echo "var is upper case"
      ;;
    +|-|/|%)
      echo "var is symbol"
      ;;
    *)
      echo "default"
      ;;
esac

一般的な慣習として、デフォルトケースは一番最後に*として定義します。

It’s a common idiom to use ‘*’ as the final pattern to define the default case, since that pattern will always match.

(( 算術式 ))

(( 算術式 ))は、算術式を評価し終了ステータスを返します。終了ステータスは、式の結果が0以外のときは0(真)、それ以外のときは1(為)になります。

$ i=5; while ((i--)); do echo $i; done

[[ 条件式 ]]

[[ 条件式 ]]は、条件式を評価し終了ステータスを返します。終了ステータスは、評価が真(true)のときは0(真)、為(false)のときは1(為)になります。

$ f="apple"; if [[ $f == [aA]pple ]]; then echo "match"; else echo "unmatch"; fi
match

シェルパラメータ (Shell Parameters)

変数の割り当て

変数の割り当ては変数=値で行います。値を定義しなかった場合はnull文字が設定されます。
${#変数}と書くと文字列の長さを取得できます。

$ message="Hello World"
$ echo "$message"
Hello World
$ echo "${#message}"
11
$ message=
$ echo "$message"

$ echo "${#message}"
0

変数の属性 (Attributes )

declareはビルトインコマンドで、変数の定義と同時に属性を設定することができます。
-rオプションは変数を読み取り専用にします。

$ declare -r message="Hello Wolrd"
$ message=""
bash: message: readonly variable

readonlyコマンドでも読み取り専用の変数を定義できます。

$ readonly message="Hello World"

変数の定義と読み取り専用化は別々に行うこともできます。

$ message="Hello World"
$ readonly message

属性の確認

-pオプションを指定すると属性と値を表示します。

$ declare -p message
declare -r message="Hello World"

数値

-iオプションを指定すると数値(intger)として定義されます。
数値として定義すると後述する算術式を使わなくても数値計算が行えます。

$ declare -i num=0
$ num=num+10
$ echo $num
10

数値の場合、${#変数}は桁数を返します。

$ declare -i num=1000
$ echo ${#num}
4

配列

-aオプションを指定すると配列として定義されます。
添え字に@を指定すると配列全体を参照します。

$ declare -a array=(a b c)
$ echo "${array[@]}"
a b c
$ echo "${#array[@]}"
3
$ array[0]=A
$ echo "${array[@]}"
A b c

配列をfor文で走査する場合は次のように記述します。

for i in ${array[@]}
do
  echo "$i"
done

添え字を使いたい場合は次のようにfor文を記述します。
添え字の変数は先頭に$を付けなくても構いません(付けても動きます)。

for ((i = 0; i < ${#array[@]}; i++)) {
  array[i]=${array[i]^^}
  echo "$i=${array[i]}"
}

シェル展開 (Shell Expansions)

ブレース展開 (Brace Expansion)

$ echo a{1,2,3}
a1 a2 a3

ダブルクォーテーション内のブレース展開はされません。

$ echo "a{1,2,3}"
a{1,2,3}

シーケンス式 (sequence expression)

シーケンス式 {x..y} を使って連番を生成できます。

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

{x..y..incr}という書き方で増分を指定できます。

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

数値以外に文字も指定できます。

for i in {a..f}; do echo $i; done

シェルパラメータ展開 (Shell Parameter Expansion)

基本形式 (The basic form)

parameterの値が展開されます。

${parameter}

${paramter:-word}

変数 parameter が未定義かnull文字の場合、代わりに word が展開されます。

$ now=
$ echo ${now:-$(date)}
Sun Apr 28 16:27:40 UTC 2019

${parameter:=word}

変数 parameter が未定義かnull文字の場合、代わりに word が展開されparameterに代入されます。

$ now=
$ echo ${now:=$(date)}
Sun Apr 28 16:27:40 UTC 2019
$ echo $now
Sun Apr 28 16:27:40 UTC 2019

${parameter:?word}

変数 parameter が未定義かnull文字の場合、wordの展開された結果が標準エラーに出力されます。wordが未定義の場合は、代わりのメッセージが出力されます。

$ now=
$ echo ${now:?}
bash: now: parameter null or not set
$ now=
$ echo ${now:?"is null"}
bash: now: is null

${parameter:+word}

変数 parameter が定義されている(未定義かnull文字以外)場合、word が展開されます。それ以外の場合は空文字が展開されます。

$ now=$(date "+%Y/%m/%d")
$ echo ${now:+"today is $now"}
today is 2019/04/30

部分文字列展開 (Substring Expansion)

${parameter:offset}

$ var=0123456789abc
$ echo ${var:5}
56789abc

${parameter:offset:length}

$ var=0123456789abc
$ echo ${var:5:3}
567

文字数 ${#parameter}

parameterの文字数を展開します。

$ var=20190429
$ echo ${#var}
8

パターンに一致する部分を削除

parameterからwordに一致する部分を削除します。

${parameter#word}

parameterの左側からwordで指定するパターンに最短一致する部分を削除して展開します。

${parameter##word}

parameterの左側からwordで指定するパターンに最長一致する部分を削除して展開します。

$ var=123123123abc456456456
$ echo ${var#12*3}
123123abc456456456
$ echo ${var##12*3}
abc456456456

${parameter%word}

parameterの右側からwordで指定するパターンに最短一致する部分を削除して展開します。

${parameter%%word}

parameterの右側からwordで指定するパターンに最長一致する部分を削除して展開します。

$ var=123123123abc456456456
$ echo ${var%45*6}
123123123abc456456
$ echo ${var%%45*6}
123123123abc

パターンに一致する部分を置換

parameterからpatternに一致する部分をstringで置換します。

${parameter/pattern/string}

最短一致

$ var=123123123abc456456456
$ echo ${var/[[:digit::]]/*}
*23123123abc456456456

${parameter//pattern/string}

最長一致

$ var=123123123abc456456456
$ echo ${var//[[:digit::]]/*}
*********abc*********

コマンド置換 (Command Substitution)

バッククォート

従来からある記法です。

$ NOW=`date "+%Y/%m/%d %H:%M:%S"`
2019/04/28 05:29:56

$( )

新しい記法です。

$ NOW=$(date "+%Y/%m/%d %H:%M:%S")
2019/04/28 05:31:27

算術展開 (Arithmetic Expansion)

$(( 算術式 ))

$ x=10
$ y=5
$ echo $x $y
10 5
$ echo "x + y = $(( x + y ))"
x + y = 15

Bash組み込みコマンド (Bash Builtin Commands)

echo

  • オプション -n : 改行(newline)が抑制されます。
  • オプション -e : エスケープキャラクタが有効になります。
syntax
echo [-neE] [arg …]
$ echo -n "abc"; echo "def"
abcdef
$ echo "Hello\tWolrd"
Hello\tWolrd
$ echo -e "Hello\tWolrd"
Hello   Wolrd

printf

  • オプション -v var : 出力結果を変数varに格納します。
syntax
printf [-v var] format [arguments]
echo "+-----+-+-+-+----------------------------------------------------+"
echo "| no  |f|d|s|file                                                |"
echo "+-----+-+-+-+----------------------------------------------------+"

declare -i i=1
for name in $(find /etc)
do
  f=""
  if [ -f "$name" ]; then
    f="o"
  fi
  d=""
  if [ -d "$name" ]; then
    d="o"
  fi
  l=""
  if [ -L "$name" ]; then
    l="o"
  fi
  printf "| %3d |%s|%s|%s| %-50s |\n" $i ${f:-" "} ${d:-" "} ${l:-" "} $name
  ((i++))
done

echo "+-----+-+-+-+----------------------------------------------------+"

source

filenameで指定したシェルスクリプトを読み込みます。.はsourceの別名です。

syntax
source filename

補足

vim

Ubuntuにvimをインストールする

# apt-get update
# apt-get install -y vim

vimのカラースキーマをセットする
使えるカラースキーマを確認

# ls -1 /usr/share/vim/vim*/colors/
README.txt
blue.vim
darkblue.vim
default.vim
delek.vim
desert.vim
elflord.vim
evening.vim
industry.vim
koehler.vim
morning.vim
murphy.vim
pablo.vim
peachpuff.vim
ron.vim
shine.vim
slate.vim
torte.vim
zellner.vim

.vimrcに下記の行を追加
colorschemeに使用するカラースキーマ名を設定

.vimrc
syntax on
colorscheme blue

blue.png

ちなみに"default"
default.png

"evening"
evening.png

カラースキーマはVim Colorsで探せます。

rubytomato@github
今までJavaをメインにやってきましたが、JavaScript(Node.js)の習得に取り組み始めました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした