ホーム » スタッフ » 斉藤徹 » 講義録 » 情報メディア工学 » シェルスクリプトの演習

2023年7月
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

検索・リンク

シェルスクリプトの演習

今回は、前回までのシェルの機能を使って演習を行う。

プログラムの編集について

演習用のサーバに接続して、シェルスクリプトなどのプログラムを作成する際のプログラムの編集方法にはいくつかの方式がある。

  • サーバに接続しているターミナルで編集
    • nano , vim , emacs などのエディタで編集
  • パソコンで編集してアップロード
    • scp 命令で編集したファイルをアップロード
  • パソコンのエディタのリモートファイルの編集プラグインで編集
    • VSCode の remote-ssh プラグインを使うのが簡単だけど、サーバ側の負担が大きいので今回は NG

リモート接続してエディタで編集

今回の説明では、emacs で編集する方法を説明する。

((( Emacs を起動 )))
guest00@nitfcei.mydns.jp:~$ emacs helloworld.sh

エディタが起動すると、以下のような画面となる。

scpでファイルをアップロード

scpコマンドは、ssh のプロトコルを使ってネットワークの先のコンピュータとファイルのコピーを行う。前述の emacs などのエディタが使いにくいのなら scp を使えばいい。

((( scp 命令の使い方 )))
$ scp ユーザ名@ホスト名:ファイルの場所 

((( サーバの helloworld.sh をダウンロード )))
C:\Users\t-saitoh> scp -P 443 guest00@nitfcei.mydns.jp:helloworld.sh .
C:\Users\t-saitoh> scp -P 443 guest00@nitfcei.mydns.jp:/home0/Challenge/3-shellscript/helloworld.sh .
((( パソコンの hoge.sh をアップロード )))
C:\Users\t-saitoh> scp -P 443 hoge.sh guest00@nitfcei.mydns.jp:
((( パソコンの hoge.html を public_html にアップロード ))) 
C:\Users\t-saitoh> scp -P 443 hoge.html guest00@nitfcei.mydns.jp:public_html

シェルスクリプトの命令

条件式の書き方

シェルには、test コマンド( [ コマンド ) で条件判定を行う。動作の例として、テストコマンドの結果を コマンドの成功/失敗 を表す $? を使って例示する。

guest00@nitfcei:~$ [ -f helloworld.sh ] ; echo $?    # [ -f ファイル名 ]
0                                                    # ファイルがあれば0/なければ1
guest00@nitfcei:~$ [ -x /bin/bash ]; echo $?         # [ -x ファイル名 ]
0                                                    # ファイルが存在して実行可能なら0/だめなら1
guest00@nitfcei:~$ [ -d /opt/local/bin ] ; echo $?   # [ -d ディレクトリ名 ]
1                                                    # ディレクトリがあれば0/なければ1
guest00@nitfcei:~$ [ "$PATH" = "/bin:/usr/bin" ] ; echo $?   # [ "$変数" = "文字列" ]
1                                                    # $変数が"文字列"と同じなら0/違えば1

シェルの制御構文

((( シェルの if 文 )))
if [ -f helloworld.sh ]; then
   echo "exist - helloworld.sh"
elif [ -f average.c ]; then
   echo "exist - average.c"
else
   echo "みつからない"
fi
((( シェルの for 文 )))
for user in /home0/guests/*   # ワイルドカード文字 * があるので、/home0/guests/ のファイル一覧
do                            # が取り出されて、その1つづつが、$user に代入されながら繰り返し。
    echo $user
done
---
結果: /home0/guests/guest00, /home0/guests/guest01 ... 
((( while 文 )))
/bin/grep ^guest < /etc/passwd \    # passwd ファイルでguestで始まる行を抜き出し、
| while read user                   # read コマンドで その 行データを $user に代入しながらループ
  do
      echo $user
  done

シェル演習向けのコマンド一例

`コマンド`と$(コマンド)

((( コマンドの結果を使う )))
guest00@nitfcei:~$ ans=`whoami`     # whoami コマンドの結果を ans に代入
guest00@nitfcei:~$ echo $ans        # バッククオートに注意 ' シングルクオート " ダブルクオート ` バッククオート
guest00
guest00@nitfcei:~$ ans=$(pwd)       # pwd コマンドの結果を ans に代入
guest00@nitfcei:~$ echo $ans        # 最近は、$(コマンド) の方が良く使われている
/home0/guest00

コマンドライン引数

シェルの中でコマンドライン引数を参照する場合には、”$数字“, “$@” を使う。$1 , $2 で最初のコマンドライン引数, 2番目のコマンドライン引数を参照できる。すべてのコマンドライン引数を参照する場合には、$@ を使う。

((( argv.sh : コマンドライン引数を表示 )))
#!/bin/bash
echo "$@"
for argv in "$@"
do
    echo "$argv"
done
((( argv.sh を実行 )))
guest00@nitfcei:~$ chmod 755 argv.sh
guest00@nitfcei:~$ ./argv.sh abc 111 def
abc 111 def          # echo "$@" の結果
abc                  # for argv ... の結果
111
def

cutコマンドとawkコマンド

((( 行の特定部分を抜き出す )))
guest00@nitfcei:~$ cut -d: -f 1 /etc/passwd   # -d:  フィールドの区切り文字を : で切り抜き
root                                          # -f 1 第1フィールドだけを出力
daemon
adm
:
guest00@nitfcei:~$ awk -F: '{print $1}' /etc/passwd  # -F: フィールド区切り文字を : で切り分け
root                                                 # ''
daemon
adm
:

lastコマンド

((( ログイン履歴を確認 )))
guest00@nitfcei:~$ last
t-saitoh pts/1        64.33.3.150      Thu Jul  7 12:32   still logged in
最近のログインした名前とIPアドレスの一覧
:
((( guest* がログインした履歴 )))
guest00@nitfcei:~$ last | grep guest
guest15  pts/11       192.156.145.1    Tue Jul  5 16:00 - 16:21  (00:21)
:
((( 7/5にログインしたguestで、名前だけを取り出し、並び替えて、重複削除 )))
guest00@nitfcei:~$ last | grep guest | grep "Jul  5" | awk '{print $1}' | sort | uniq
7/5("Jul  5")の授業で演習に参加していた学生さんの一覧が取り出せる。
### あれ、かなりの抜けがあるな!?!? ###

whoisコマンド

((( IPアドレスなどの情報を調べる )))
guest00@nitfcei:~$ whois 192.156.145.1
:
inetnum:        192.156.145.0 - 192.156.148.255
netname:        FUKUI-NCT
country:        JP
:
guest00@nitfcei:~$ whois 192.156.145.1 | grep netname:
netname:   FUKUI-NCT
netname:   ANCT-CIDR-BLK-JP

シェルスクリプトのセキュリティ

ここまでのプログラムの動作例では、a.out などのプログラムを実行する際には、先頭に “./” をつけて起動(./a.out)している。これは「このフォルダ(“./“)にある a.out を実行せよ」との意味となる。

いちいち、カレントフォルダ(“./”)を先頭に付けるのが面倒であっても、環境変数 PATH を “export PATH=.:/bin:/usr/bin” などと設定してはいけない。こういった PATH にすれば、”a.out” と打つだけでプログラムを実行できる。しかし、”ls” といったファイル名のプログラムを保存しておき、そのフォルダの内容を確認しようとした他の人が “ls” と打つと、そのフォルダの中身を実行してしまう。

guest00@nitfcei:~$ export PATH=".:/bin:/bin/bash"
guest00@nitfcei:~$ cat /home0/Challenge/1-CTF.d/Task5/Bomb/ls
#!/bin/bash

killall -KILL bash
guest00@nitfcei:~$ cd /home0/Challenge/1-CTF.d/Task5/Bomb
guest00@nitfcei:~$ ls
# 接続が切れる(bashが強制停止となったため)

こういったシェルスクリプトでのセキュリティのトラブルを防ぐために、

  • 環境変数PATHに、カレントフォルダ”./”を入れない
  • シェルスクリプトで外部コマンドを記述する際には、コマンドのPATHをすべて記載する。
    コマンドのPATHは、which コマンドで確認できる。echo とか [ といったコマンドは、bash の組み込み機能なので、コマンドのPATHは書かなくていい。

演習問題

シェルスクリプトの練習として、以下の条件を満たすものを作成し、スクリプトの内容の説明, 機能, 実行結果, 考察を記載したワードファイル(or PDF)等で、こちらのフォルダに提出してください。

  • スクリプトとして起動して結果が表示されること。(シバン,実行権限)
  • コマンドライン引数を使っていること。
  • 入出力リダイレクトやパイプなどを使っていること。
  • 以下の例を参考に。
((( 第1コマンドライン引数指定したユーザが、福井高専からアクセスした履歴を出力する。)))
#!/bin/bash

if [ -x /usr/bin/last -a -x /bin/grep ]; then   # [ ... -a ... ] は、複数条件のAND
    /usr/bin/last "$1" | /bin/grep 192.156.14
fi
-------------------------------------------------------------------------
((( guest グループで、$HOME/helloworld.sh のファイルの有無をチェック )))
#!/bin/bash

for dir in /home0/guests/*
do
   if [ -f "$dir/helloworld.sh" ]; then      # PATHの最後の部分を取り出す
      echo "$(/usr/bin/basename $dir)"       # $ basename /home0/guests/guest00
   fi                                        # guest00                  ~~~~~~~basename
done