目次
・エディタの超基礎的な操作を身につける
・シェルスクリプトについて知る
・簡単なシェルスクリプトを書いたことがある
エディタ操作
vim
- ターミナル上で動作するテキストエディタ
- viを拡張したもの
- 複数回のundoなど、viより便利な機能が追加されている
- 多くのLinux環境に標準で入っていることが多いため、この講義ではvimを使ってファイルを編集する
- 根強いファンが多い
エディタの使い方
vim ファイル名でファイルを開き、ファイルが存在しない場合は新規作成される。
vim /hoge/fuga/test.txt 開くとこんな感じ
sample line 1
sample line 2
sample line 3
sample line 4
sample line 5
~
~
~
"test.txt" 5L, 70B モード
- インサートモードに入る(カーソルがある場所から開始)基本的な記述モード
i - インサートモードから抜ける
Esc# 環境によってはEscが他の操作と競合して使えないこともある Ctrl + C
ファイル保存・終了
- 終了
# vimを閉じるコマンド :q - 保存
# 保存するコマンド(:w)+vimを閉じるコマンド(:q) :wq - 読み取り専用や他の人が開いている時に上記操作ができない場合
# 強制的に+保存するコマンド(:w!)+vimを閉じるコマンド(:q!) :wq! - 強制終了(保存されない)
# 強制的に+vimを閉じるコマンド :q!
なんかよくわからない!って時はとりあえずctrl+c → :q!
基本的な操作
- 上下左右へ移動
-
h:左 -
j:下 -
k:上 -
l:右 - 矢印キーでも可能
-
-
gg:先頭行へ移動 -
G:最終行へ移動 - 任意行へ移動
任意の行数番号 -> G # 1行目に移動する場合 1 -> G
基本時間が余った人向け
冒険してきてください。
シェルスクリプト
シェルは通常コマンドを対話的に入力します。人の手で入力すると以下のような問題が出てきます。
- 毎回手入力するとミスの恐れがある
- 連続的な操作の場合は、前のコマンド実行が完了するまで待たなければいけない
- 任意の時間(夜中とか)に実行したいとき、その時間に人が待機していないといけない
実行するコマンドをテキストファイルに記録しておき、まとめてシェルに実行させることができます。
事前に実行したいシェルスクリプトを用意して、cron(後述)等を使って任意の時間に実行することもできます。
ハンズオン①
実際に簡単なシェルスクリプトを書いてみる。
ホームディレクトリにいることを確認してください。
ホームディレクトリに以下の内容を書いたexample1.shを作成してください。
#!/bin/bash
echo 'Hello world!'
作り方
$ vim example1.sh -
iキーを押してインサートモードへ - 内容を入力
-
Escキーを押してノーマルモードへ -
:wqenterキーを順に押して、保存し終了する
このままだと実行権限がありません。作成したファイルに実行権限をつけます。
$ ls -l | grep example1.sh
-rw-r--r-- 1 ec2-user ec2-user 0 Apr 4 15:41 example1.sh
$ chmod +x example1.sh
$ ls -l | grep example1.sh
-rwxr-xr-x 1 ec2-user ec2-user 0 Apr 4 15:41 example1.sh 実行します。
$ ./example1.sh
Hello world! 練習課題①
次の出力結果になるようにexample1.shを編集してください。
$ ./example1.sh
Hello!
My name is ○○ 答え(できるだけ見ないで!)
#!/bin/bash
echo 'Hello!'
echo 'My name is ○○' シバン
先頭に書いた#!/bin/bashのこと。
テキストファイルの先頭行に書くことで、どの処理系に実行させるかを明記します。
#!/usr/bin/pythonと書いたらpythonが処理します。
今回の場合はシェルから叩いて実行しているので、シバンを書かなくても動く場合がありますが、明示すべきです。
シバンを書かない場合は/bin/bash ./example1.sh という風に実行しますが、非推奨(そもそも先頭に書くべき)です。
set -eu
シェルスクリプトを書くときに、先頭行にset -eu ととりあえず書くと良いとされています。
分からないうちはおまじないとして書いておくのが安全です。
-e シェルスクリプトを上から実行するときに、エラーがあったらその時点で打ち止めにするオプションです。
1行目でデータを作成できた前提で2行目を書いたのに、1行目でエラーが起きて2行目が想定してない状況で動いてしまう…というのを防ぐことができます。
-u 未定義の変数を使おうとしたときに打ち止めにするオプションです。
変数に入れた名前のディレクトリを消すスクリプトで
# 例:削除対象の変数が未定義だと、意図しない場所を削除する危険がある
rm -rf "/path/to/${DIR_NAME}" としたときに、うっかり変数を未定義で動かし悲惨な事になるのを防ぐことができます。
ハンズオン②
変数
シェルスクリプトでは変数を使う事が可能です。
ホームディレクトリに以下の内容を書いたexample2.shを作成してください。
#!/bin/bash
var1='Hello'
var2='World!'
echo ${var1} ${var2}
実行します。(実行権限をつけるのを忘れずに!)
$ ./example2.sh
Hello world!
配列も使えます。
ホームディレクトリに以下の内容を書いたexample3.shを作成してください。
#!/bin/bash
names=("John" "Jane" "Bob" "Alice")
echo ${names[2]}
実行します。(実行権限!)
$ ./example3.sh
Bob
シェルの実行結果を変数に格納することも可能です。
example4.shを作成してください。
#!/bin/bash
var1='今は'
var2=`date`
echo ${var1} ${var2}
実行します。(ry
$ ./example4.sh
今は Wed Apr 10 13:18:52 JST 2024 ファイル出力
example5.shを作成してください。
※後で使うので必ず作って!!
#!/bin/bash
var1=`date`
var2=`cat /proc/meminfo | grep "MemFree"`
echo ${var1} ${var2} >> /home/ec2-user/example5.txt
実行して、./example5.txtの中身を見てください。
どうやってやる?(できるだけ見ないで!)
$ ./example5.sh
$ cat ./example5.txt
Wed Apr 10 13:34:13 JST 2024 MemFree: 1050580 kB
入出力の書き方(一部)
command > file # ファイル作成 or 上書き
command >> file # 追加書き込み ファイルなければ作成
command &>> file # 標準出力・標準エラー出力を同一ファイルに追加書き込み
command >> file 2>&1 # 同上
command < file # ファイル内容をコマンドの標準入力に渡す
今回は>>で出力しています。追記されるはずなので、複数回実行して./example5.txtの中身を見てください。
if
if文で分岐させることもできます。
ホームディレクトリに以下の内容を書いたexample6.shを作成してください。
#!/bin/bash
STR1=nifty
STR2=nihuthi
if [ $STR1 != $STR2 ]; then
echo '一致していません'
else
echo '一致しています'
fi
ifの他の条件文の書き方は検索すれば出てきます。
なお、if文で[]を使うのは必須ではなく、今回は[コマンドを使っているだけです。難しい話は置いておいて、if文が使えるという事が分かればよいです。それさえ知っていれば検索で使い方は無限に出てきます。興味がある人はif文について検索してみてください。
for
繰り返し処理もできます。
以下の内容を書いたexample7.shを作成してください。
#!/bin/bash
for i in {1..10}; do
echo "Number: $i"
done 実行してください。
配列と組み合わせて使う事が多いです。
以下の内容を書いたexample8.shを作成してください。
#!/bin/bash
# 名前の配列を定義する
names=("John" "Jane" "Bob" "Alice")
# 配列の要素を1つずつ表示する
for name in "${names[@]}"; do
echo "Hello, $name!"
done 実行してください。
発展課題
時間の余った人は挑戦してみてください。まずは自分で考えてみてください。必要に応じてヒントや検索も活用しましょう。
はじめて触る人にとっては難しめです。ヒント見たり検索したりしながら解く前提の難易度にしてます。
解答例は一例です。ほかにもさまざまな書き方があります。
現在の日時をYYYY-MM-DD_HH:MM:SSの形式で表示するシェルスクリプトを書いてください。
※1行で書けるので、実はシェルスクリプトにする必要もない
ヒント
dateコマンドに引数を設定することで出力形式を指定することができます。やり方は……調べてみてください。
解答例
#!/bin/bash
current_date_time=$(date '+%Y-%m-%d_%H:%M:%S')
echo $current_date_time
システム上で利用可能なシェルの一覧を/etc/shellsファイルから取得し、それを表示するシェルスクリプトを書いてください。
※1行で書けるので、実はシェルスクリプトにする必要もない
ヒント
cat /etc/shellsで使用可能なシェル一覧を見る事ができます。
#からはじまるコメント行があるので、それを除外する必要があります。
どうやって?(追加ヒント)
grepコマンドで絞り込みをします。普通に使うと○○が含まれる行を標準するコマンドですが、オプションを使うと○○を含まない行を出すことが可能です。
ある書き方をすると、○○で始まる行以外を出すことができます。
解答例
#!/bin/bash
cat /etc/shells | grep -v '^#' 現在ログインしているすべてのユーザーのリストを表示するシェルスクリプトを書いてください。
※1行で書けるので、実はシェルスクリプトにする必要もない
ヒント
whoでログインユーザ一覧を得る事ができます。叩いてみてください。
他にもいろいろ情報があるので、スペース区切りの1つ目だけ取り出せばよいです。
どうやって?(追加ヒント)
色々やり方はありますが、awkコマンドが使えます。awkコマンドで検索してみてください。
ちなみにawkコマンドはこんな単機能ではなく、いろんなことができます。
解答例
#!/bin/bash
logged_in_users=$(who | awk '{print $1}')
echo "$logged_in_users" /home/ec2-user/example5.txtの文字数をカウントし、example5.txtの文字数は○○文字ですと表示するシェルスクリプトを書いてください。
ヒント
wc -cで文字数を得る事ができます。wc -c < example5.txtを叩いてみてください。
解答例
#!/bin/bash
# ファイル名を指定
file_name="example5.txt"
# ファイルの文字数をカウントする
char_count=$(wc -c < "$file_name")
# 結果を表示
echo "$file_name の文字数は $char_count 文字です。" シングルクォート、ダブルクォート、バッククォートの違いについて調べよ。動作の違いが分かるような簡単なシェルスクリプトを書いてください。
解答例
#!/bin/bash
# 変数の定義
NAME="John Doe"
# シングルクォートの使用
echo 'My name is $NAME' # 変数が展開されない
# ダブルクォートの使用
echo "My name is $NAME" # 変数が展開される
# バッククォートの使用
echo "The current directory is `pwd`" # `pwd`コマンドが実行され、その結果が表示される カレントディレクトリ内のすべてのファイルとディレクトリの数を別々にカウントして表示するシェルスクリプトを書いてください。
ヒント
wc -lで行数を得る事ができます。wc -l < example5.txtを叩いてみてください。
解答例
#!/bin/bash
files=$(ls -l | grep '^-' | wc -l)
dirs=$(ls -l | grep '^d' | wc -l)
echo "ファイルの数: $files"
echo "ディレクトリの数: $dirs"