TIL

Did you learn anything today?

シェルスクリプトまとめ1

リモート操作

X を飛ばす

$ ssh -Y -C chino@192.168.1.1

-Y: X の転送指定

-C: 通信内容の圧縮。付けると応答性がよくなる。

SSH 越しにコマンドを 1 つだけ実行する

$ ssh chino@server /scripts/do_backup.sh

検索

$ grep -r -i -E "(香風|かふう|カフウ) ?(智乃|ちの|チノ) | Kafu Chino" /var/share/

-i: 大文字小文字の違いを無視 -E: 正規表現を使う

仮想端末

インストール

$ sudo apt-get install tmux

有効化

$ tmux

再接続

$ tmux attach

切断

Ctrl + BD

※ 切断してから exit しよう

画面の生成

Ctrl + BC

画面の切り替え(次へ)

Ctrl + BN

画面の切り替え(前へ)

Ctrl + BP

画面の分割(縦)

Ctrl + B"

画面の分割(横)

Ctrl + B%

画面の移動

Ctrl + B, ,

画面サイズの変更

Ctrl + BCtrl + , , ,

画面のスクロールの有効

Ctrl + B[

Qで元の状態に戻る。

履歴

検索機能(後方検索)

Ctrl + R

逆方向(前方検索)にもコマンド履歴の検索をするには・・・

.bashrcstty stop endefを追記する。

検索機能(前方検索)

Ctrl + S

端末間でコマンド履歴を共有する

.bashrcに追記

function share_history {
    history -a
    history -c
    history -r
}
PROMPT_COMMAND='share_history'
shopt -u histappend
export HISTSIZE=9999

iandeth. - bash にて複数端末間でコマンド履歴(history)を共有する方法

scp

ローカルのfile.txtを 192.168.1.1 の/tmp配下にコピーする。

$ scp ./file.txt chino@192.168.1.1:/tmp/

ログインに使ったユーザのホーム

chino@192.168.1.1:~/

他のユーザのホーム

chino@192.168.1.1:~Cococa/

相対パス・・・ログイン後のホームディレクトリが起点になる

chino@192.168.1.1:../../tmp/

リモートのfile.txtを ローカルにコピーする。

$ scp chino@192.168.1.1:/tmp/file.txt ~/

ディレクトリごとコピーする

$ scp chino@192.168.1.1:/tmp/*.log /tmp/

$ scp -r chino@192.168.1.1:/tmp/results/ /tmp/

サーバからサーバにファイルをコピーする

$ scp chino@rabbithouse:/data/file chiya@amausaan:/backup/

システムの過負荷

top

システムの負荷に関する情報を、数秒間隔でリアルタイムに更新しにながら表示

load average: 負荷の指標。CPU に処理されるのを待ってる仕事の数。1 分あたり平均で何個分の仕事が積み上がっているのかを表す値。CPU のコア数以下ならよい。

%CPU: CPU 使用率。

TIME+: CPU 時間。実際に CPU を使ってる時間。

過負荷の原因を探るときは「CPU 使用率が高い」、「CPU 時間も長い」プロセスがあるかどうかを見る。

Cキーを押すとコマンドの詳細表示が切り替わる。

PID: プロセス ID

$ sudo kill プロセスID

Shift + P・・・CPU 使用率でソート

Shift + T・・・CPU 時間順でソート

Shift + M・・・メモリー使用量順でソート

ログファイルの閲覧

$ grep "/redmine" access.log | grep -v "/live" |  less

-v: 指定の文字列を含む行を除外する

圧縮されたログファイルを grep

zcat: 圧縮ファイルを読み込んで内容を展開しながら出力する(gzip 形式専用)

$ zcat access.log.gz | grep "/redmine" access.log | grep -v "/live" |  less

zcat: .gz, .tgz 形式

xzcat: .xz 形式

unzip -p: zip 形式

リアルタイム出力

$ tail -F access.log | grep "/redmine" access.log | grep -v "/live"

シェルスクリプト

  1. シェルスクリプトを作成する。

    $ vim setup.sh

  2. 実行権限を変更する。(新しく作ったシェルスクリプトはまだ実行権限が付与されていない)

    $ chmod +x setup.sh

  3. シェルスクリプトを実行する

ファイル名だけでコマンドとして実行できるのは、ファイルが/bin//usr/bin/などの特別な場所にあるときのみ

$ /home/chino/setup.sh
$ ./setup.sh
$ ~chino/setup.sh

変数

#!/bin/bash

base=/var/log/nginx
latest=${base}/access.log
prev=${base}/access.log.1.gz

cat $latest | analyze_latest.sh max
zcat $prev | analyze_prev.sh max
tar xvf log_result_max

tar xfv <path>: ファイルを展開

tar cfv <path>: ファイルを圧縮

環境変数

自分で定義しなくても$変数名と書くと値を参照できる特殊な変数。

$HOME: ホームディレクトリのパス $PWD: 現在のディレクトリのパス $EDITOR: 既存のテキストエディタのパス $PAGER: 既存のページャー(less, lv など)のパス $USER: 現在のユーザのユーザ名 $GROUP: 現在のユーザののグループ名 $HOSTNAME: そのマシンのホスト名

コマンド置換

$(コマンド列)または`コマンド列`

$ mv result.txt result-$(date +%Y-%m-%d).txt

必要な情報を取り出す

cut・・・パイプで渡された内容の各行から必要な部分を切り取って返す

$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | less

"-d": 区切り文字。(一文字だけしか指定できない)

"-f": 取り出す位置

アクセスされた日付だけを取り出す。

$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d "[" -f 2 | cut -d "]" -f 1 | less

同じ内容の行を数えたい

uniq・・・連続した重複を取り除く

sortしておけば、すべての重複をきちんと取り除ける。

uniq -c・・・それぞれの内容が何回登場したか、が出力される。

アクセスされたページのパスの一覧に対して、同じパスの登場回数を数える

$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | sort -r | uniq -c | cat

-r: 降順ソート

上位と下位の項目を抽出する

上位

$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | sort -r | uniq -c | head -n 20

-n: 出力する行数を変える

下位

$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | sort -r | uniq -c | tail -n +6

-n +: 指定行数以降を出力する。

-n -: 指定行数以外を除いた全ての行を出力する。

CSV ファイルの並び替え

1,2,3,4,6 列目を取り出す。

$ cat test.csv | cut -d "," -f 1-3,4,6 | sort -t "," -k 3 -n | cat

-t: 区切り文字の指定

-k: 指定した列の内容で並び替え

-n: 数の部分を数値として解釈して数の大小で並べ替える

※ cut と sort でオプションの名前が違う。

結果を上書き出力

$ cat test.csv | cut -d "," -f 1-3,4,6 | sort -t "," -k 3 -n > sorted.csv

>>: 追記

オプション引数

#!/bin/bash
source=$1
target=$2
$ ./script.sh first second third

first・・・$1

second・・・$2

third・・・$3

名前付き引数

#!/bin/bash

while getopts b: OPT
do
    case $OPT in
        b) base="$OPTARG" ;;
        n) next="$OPTARG" ;;
        p) previous="$OPTARG" ;;
        o) output="$OPTARG" ;;
    esac
done
$ ./opt_script.sh -b /var/log/nginx/access.log2.gz -n /var/log/nginx/access.log.1.gz

処理分岐

$ script.sh foo bar
if [ $# = 2 ] # 引数の個数が2に等しい
then
    echo "Hello!"
else
    echo "Good afteroon"
fi
target=$1

if [ "$target" != "" ] # 引数が渡された
then
    echo "Hello!"
else
    echo "Good afteroon"
fi

繰り返し

#!/bin/bash

for filename in rabbithouse.log fleur_du_lapin.log amausaan.log
do
    ./create-report.sh $filename
done
#!/bin/bash

for filename in `cd /var/log/nginx; ls *.log | grep -v error.log`
do
    ./create-report.sh $filename
done

シェル関数

#!/bin/bash

# 主要な処理をmain関数として先に定義しておいて最後にそれを呼び出すのが定石
main() {
    report rabbithouse.log rabbithouse-${today}.csv /shared/rabbithouse/reports
    report fleur_du_lapin.log ${today}.csv /shared/fleur_du_lapin/reports
    report amausaan.log ${today}.txt /shared/amausaan/reports
}

today() {
    date +%Y-%m-%d
}

report() {
    source=$1
    report=$2
    outdir=$3
    ./analyze_mail_log.sh $source $report
    mkdir -p $outdir
    mv /tmp/${report} ${outdir}/
    echo "$source を処理しました"
}

main

exit 1: スクリプトの実行を中断して終了ステータスを 1 にする

return 1: 関数の実行を中断して終了ステータスを 1 にする

※ 関数のなかで$1と書くと、シェルスクリプトの引数ではなく、関数の引数を参照する。ので、関数内で引数を参照するときは関数外でいったん別の名前の変数として定義しておく。