第40回勉強会(2015/02/21) - 長岡 IT開発者 勉強会(NDS)
はじめに
Bourne Shell系の内容です。C Shell系ではないので注意してください。
NOTE:
特に明言しない場合は ash(dash) 準拠となります。
シェルの基礎
コマンド
ブランク区切りの単語のリストです。
find . -type f
ls -la
最初の単語は実行するコマンドとなります。残りの単語はコマンドの引数となります。
NOTE:
変数代入 => ブランク区切りの単語リスト => リダイレクション => 制御演算子
foo=FOO /bin/sh -c 'echo $foo' > foo.txt && echo "done"
コマンドに対して各種展開(チルダ展開、パラメータ展開、コマンド置換、算術式展開、クォート除去など)が行われます。
echo ~
echo ${USER:-unknown}
$(echo "echo foo")
echo $(( 1 + 1 ))
echo "foo bar baz"
変数
値を保持するためのものです。下記のようにして値を代入します。
name=[value]
valueが指定されなかった場合は、値は空文字となります。
$name
や ${name}
で展開されます(パラメータ展開)。
name="hello.txt"
echo $name
echo ${name}
NOTE:
ほかにもデフォルト値を指定したりパターンに一致した部分を取り除いたりして展開する形式などもあります。"Parameter Expansion" や 「パラメータ展開」 で調べてみてください。
echo ${name:=hello.world.txt}
hello.txt
echo ${name##*.}
txt
echo ${name%.*}
hello.world
パイプとリダイレクト
パイプ (|) はコマンドの標準出力とコマンドの標準入力を接続します。
ps ux | grep vim | grep -v grep
リダイレクト (>, >>, >&, <, <<, etc.) はコマンドの入出力をファイルに対して行うことができます。
ps ux >out.txt
ps ux >>out.txt
curl -v localhost:5000 >out.txt 2>&1
nc localhost 5000 <in.txt
cat <<EOF >out.txt
foo
bar
buz
EOF
NOTE:
リダイレクトはコマンド実行前に処理されるので、処理するファイルとリダイレクト先のファイルが同じ場合などに注意が必要です。
cat hoge.txt >hoge.txt
リダイレクトについてははこちらに詳しく解説されています。参考にしてください。
制御演算子
コマンド同士を ; で区切ることでコマンドを順番に実行できます。
date; pwd; ls
コマンド同士を && (ANDリスト) で区切ることで前のコマンドの終了ステータスが 0 の場合に後のコマンドが実行されます。
test -e file.txt && cat file.txt
コマンド同士を || (ORリスト) で区切ることで前のコマンドの終了ステータスが 0 以外の場合に後のコマンドが実行されます。
test -e file.txt || touch file.txt
制御構文
if
条件によって処理を分岐させたい場合に使用します。
if [ $(id -u) -eq 0 ]
if> then
if> echo "rootuser"
if> else
if> echo "user"
if> fi
NOTE:
id -u | awk '{if($1==0){ print "rootuser" }else{ print "user" }'
ちなみに [
もコマンドです(testコマンド)。引数が $(id -u) -eq 0 ]
となります。
for
与えられた引数の数だけ処理を繰り返し実行したい場合に使用します。
for f in $(ls)
for> do
for> cp $f $f.org
for> done
NOTE:
ls | xargs -I@ cp @ @.org
case
値によって処理を分岐させたい場合に使用します。
case $(read -p 'yN? ' yn; echo $yn) in
case> [yY]* ) echo 'yes!' ;;
case> *) echo 'no...' ;;
case> esac
NOTE:
read -p 'yN? ' yn; echo $yn | awk '{if(tolower($1) ~ /^(y|yes)$/){ print "YES!!" }else{ print "NO..." }}'
while
条件が真である間、処理を繰り返し実行したい場合に使用します。
while read -p '>> ' line && test "$line" != "end"
while> do
while> echo "input: " $line
while> done
コマンドのグループ化
コマンドを括弧で括るとコマンドをグループ化できます。グループ化することで出力をまとめることができます。
(echo hoge; echo fuga; echo piyo) | xargs
(echo hoge; echo fuga; echo piyo) > out.txt
この括弧で括られたコマンドはサブシェルで実行され、シェルの環境に影響を与えるような操作はコマンド終了後に影響を残しません。
hoge=HOGE; (hoge=FUGA; echo $hoge); echo $hoge
cmd="date,pwd,ls"; (IFS=$','; for c in $cmd; do $c; done)
(cd /path/to/app/src; make && make test)
NOTE:
パイプやリダイレクト先もサブシェルで動作するので変数の変更は反映されません。注意してください(「while
にパイプで渡す」など)。
シェルスクリプト
1行目にシバン(shebang)を記述し、2行目以降にコマンドを記述します。
#!/bin/sh
set -eu
if [ $# -ne 1 ]; then
echo "Usage: $0 FILE"
exit
fi
data=$(cat $1)
res=$(echo -ne "HTTP/1.0 200 OK\nContent-Type: $(echo $file | file --mime-type -b -)\n\n$data")
while true; do
echo -n "$res" | nc -q 1 -l 8080
done
NOTE:
シバンにはスクリプトを読み込むインタプリタを指定します。シェルスクリプトのファイル本体はインタプリタへのパラメータとなります。
/bin/sh
はOSによって異なります(CentOS系ではbash、Debian系ではdash)。dash向けのスクリプトであればほとんどのUNIX系OSで動作しますがbash向けの場合はバージョン違いなどで互換性がない場合があります。同一のスクリプトを複数のOS動作させたい場合は注意してください。
SSH Tips
踏み台
踏み台サーバーを経由してリモートホストに接続する場合は ProxyCommand
オプションを利用すると便利です。
-
コマンドラインオプション
ssh -o ProxyCommand="ssh -W %h:%p USER@BASTION" USER@REMOTE
-
~/.ssh/config
Host bastion User user Host remote User user ProxyCommand ssh -W %h:%p bastion
NOTE:
いちど踏み台サーバーにログインし、再度sshでリモートホストに接続しなくて良いので嬉しいですね。
ちなみに踏み台サーバーのことは bastion server と呼ぶらしいです。
ProxyCommand を指定するのが面倒なときは
ssh -t USER@BASTION ssh USER@REMOTE
で代替。
リモートホストでパスワード入力など、ターミナルでの操作必要な場合(e.g. sudo
とか su
とか)はsshの -t
(-tt
) オプションを指定します。
ssh -t REMOTE_HOST 'su - -c "ls -la /root"'
デモする場合はこんな感じで...
cat <<EOF >Dockerfile
FROM debian
RUN apt-get -y update
RUN apt-get -y install openssh-server && apt-get clean
RUN service ssh start && service ssh stop
RUN useradd -m -s /bin/bash demo
RUN echo root:root | chpasswd
RUN echo demo:demo | chpasswd
CMD ["/usr/sbin/sshd", "-D"]
EOF
docker build -t nds40/sshd .
SSHD_BASTION=$(docker run -d nds40/sshd)
SSHD_DEST=$(docker run -d nds40/sshd)
SSHD_BASTION_IP=$(docker inspect -f '{{.NetworkSettings.IPAddress}}' $SSHD_BASTION)
sudo pipework br1 $SSHD_BASTION 192.168.1.1/24
sudo pipework br1 $SSHD_DEST 192.168.1.11/24
ping 192.168.1.11 # FAIL
ssh -o ProxyCommand="ssh -W %h:%p demo@$SSHD_BASTION_IP" demo@192.168.1.11
# 後片付け
docker rm -f $SSHD_BASTION $SSHD_DEST
sudo ip link dev br1 down
sudo brctl delbr br1
リモートホストのコマンド実行結果を手元で利用
例: リモートホストのログを手元にコピーしつつ検索
ssh REMOTE_HOST 'cat /var/log/httpd/access_log*' | tee REMOTE_HOST.access_log | grep 404
手元のコマンド実行結果をリモートホストで利用
例: 手元のシェルスクリプトをリモートホストで実行
cat batch.sh | ssh REMOTE_HOST 'cat | /bin/sh'
操作ログを残す
scriptコマンドを利用して操作ログをファイルに残します。
script -q -c "ssh REMOTE_HOST" /path/to/log/file
下記のようなスクリプトを用意しておくと良いでしょう。
cat <<EOS >sshl && chmod +x sshl
#!/bin/bash
exec script -q -c "ssh $1" $1.$(date +%Y%m%d_%H%M%S).log
EOS
困ったら
-
-h
,--help
オプションでコマンドのヘルプを読みましょう -
man COMMAND
でコマンドのマニュアルを読みましょう - ググれば大抵のことは解決できます
- 「はじめてUNIXで仕事をする人が読む本」(amazon)
- process-book
NOTE:
man 1 COMMAND
で汎用コマンドとしてマニュアル参照。
環境について
Windowsの環境の方は操作やコマンドに慣れるためにも、仮想化ソフトウェアなどで手元にLinux環境を用意して作業を行うことをおすすめします。
+-----------+
| Windows |
| |
| +-------+ | SSH +-------------+
| | Linux +------||-----> REMOTE_HOST |
| +-------+ | +-------------+
| ^ |
+-----|-----+
|
+--+---+
| User |
+------+
個人的なおすすめは Vagrant による環境構築です。Vagrantfile(とプロビジョニング用のファイルなど)を共有することで、別々のマシンでも同様の環境を構築することが可能です。
NOTE:
まさか本番サーバー上で検証作業をしたり運用スクリプトを書いたりテストしたりしていませんよね...
Enjoy!
日常業務をより効率化するにはシェルの基礎のほかに各種コマンドの理解も必要となります。
次のステップではsed/awkを中心に代表的なコマンドの使い方を学びましょう。