hayashier Tech Blogs
  • hayashier Tech Blogs
  • Author's Books
    • 実践Redis入門 (日本語版)
    • 実践Redis入門 (한국어판)
  • Top Contents
    • Dive Deep Redis
    • Dive Deep Memcached
    • Kubernetes 入門
    • TCP 入門
    • TLS 入門
    • GPG 入門
    • サービス障害が発生した場合の対応方法
    • よく使うトラブルシューティング コマンド実行例 まとめ
    • コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)
  • Containers
    • Docker 入門
    • Nomad 導入
    • Dockerを利用してさっと検証環境構築
  • Kubernetes
    • Kubernetes 入門
    • Kubernetes 導入 with Amazon Linux 2
    • EKSを利用してKubernetesでSpring MVCをデプロイ (NLB + Auto Scaling)
  • Load Balancer
    • ALB 認証 導入
    • TLS extensions support with ALB
    • ELB(CLB,ALB,NLB)の種類ごとのHTTPレスポンスの違い
    • ELB(CLB) で WebSocket 通信
  • RDBMS
    • PostgreSQL DBA 入門
    • RDBMS Benchmark Get Started
    • RDBMS サンプルデータ生成 Get Started
    • RDS PostgreSQL Extensions Get Started
    • RDBMS Engine Inspection for Troubleshooting
  • Redis
    • Dive Deep Redis ~ 入門から実装の確認まで
    • Dive Deep Redis Internals ~ GETコマンド実行時の動作 ~
    • RedisのString型は今でも本当に512MBが上限か?
    • Redis 公式ドキュメント まとめ
    • Redis / Memcached Source Code Reading - Overview -
  • Memcached
    • Dive Deep Memcached ~ 入門から実装の確認まで ~
    • Dive Deep Memcached ~ SETコマンド実行時の動作 ~
    • Memcached 公式ドキュメント まとめ
    • memtier_benchmark + memcached-tool の導入
    • Redis / Memcached Source Code Reading - Overview -
  • Hadoop
    • Hadoop Get Started
  • Networking
    • TCP 入門
    • TLS 入門
    • ksnctf: HTTPS is secure, Writeup (TLS 通信解読)
    • オンプレ側ルーター(Cisco 1812J, Juniper SRX210, YAMAHA RTX 1210)から Direct Connect へ BGP 設定
  • Software
    • アルゴリズムとデータ構造 入門
    • デザインパターン 入門
    • ソフトウェアテスト 入門
  • System Admin
    • Shell Script 入門
    • サービス障害が発生した場合の対応方法
    • よく使うトラブルシューティング コマンド実行例 まとめ
    • コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)
    • GPG 入門
    • Operation Misc
  • Development
    • ローカル環境のプログラミング言語のバージョンを切り替え macOS
    • /usr/local/Cellar/pyenv/1.2.21/libexec/pyenv: No such file or directoryのエラーの対処方法
  • AWS
    • AWS Misc
    • AWS CLI, AWS SDKのリトライ処理の実装について
    • AWS CLI バージョンアップでエラー発生を解消
    • Elastic Beanstalkで稼働しているアプリケーション(Ruby, Sinatra)をAmazon Linux AMIからAmazon Linux2へ移行
    • Elastic Beanstalkでインスタンス入れ替え後にnginxのデフォルトの画面が表示されてしまう問題の対応
    • Amazon Lightsail に SSL 証明書設置 with Let's Encrypt (自動更新)
    • Amazon Lightsailで10分で作るお手軽Markdownで書く独自ドメインのブログサイト構築
    • Lambdaをローカルでテスト(with Docker)
    • ECS + ALB でダウンタイムなしでデプロイ
    • `Repository packages-microsoft-com-prod is listed more than once in the configuration`のメッセージの解消方法
  • Others
    • Pandoc 導入
    • textlint + prh による文章校正
    • 紙書籍をPDFに変換
    • Sphinx 導入
    • さくっとPocketのブックマークをはてなブックマークに移行
    • Macが突然起動しなくなった話
    • Macでターミナルが開かない (zsh編)
    • ホスト型 IDS Tripwire とネットワーク型 IDS Snort の導入 with CentOS 6
    • JMeter 導入
    • Squid 導入 with Amazon Linux AMI
    • Spring MVCを導入 (+ MySQL, Redis)
    • 外資系企業で働いている場合の確定申告方法 (RSU考慮)
Powered by GitBook
On this page
  • Shell Script 入門
  • Basic
  • 特殊変数
  • 文字列
  • メタ文字セット
  • 引数
  • AWKとsed
  • jq
  • デバッグ
  • Others
  • Others
  1. System Admin

Shell Script 入門

Shell Script 入門

Basic

制御構文

  • if

i=10
if [ $i -le 5 ]; then
    i=`expr $i + 5`
elif [ $i -le 10 ]; then
    i=`expr $i + 10`
else
    i=`expr $i + 1`
fi
  • for

max=10
for ((i=0; i < $max; i++)); do
    echo $i
done
to_val=`expr 30 - 10 + 1`
for i in `yes "" | cat -n | head -30 | tail -$to_val`; do
    :
done
  • while

i=10
while [ $i -le 30 ]; do
    i=`expr $i + 5`
done
ls file* |
while read f ; do
   cp $f $f.backup
done

read varのようにすると入力を受付け、変数varに格納される。

  • case

/etc/rc0.d/S01halt

case "$0" in
   *halt)
        message=$"Halting system..."
        command="/sbin/halt"
        ;;  
   *reboot)
        message=$"Please stand by while rebooting the system..."
        command="/sbin/reboot"
        kexec_command="/sbin/kexec"
        ;;  
   *)  
        echo $"$0: call me as 'halt' or 'reboot' please!"
        exit 1
        ;;  
esac

条件判定部分は正規表現が使える。

  • until

i=10
until [ $i -le 5 ]; do
    i=`expr $i - 1`
    echo $i
done

特殊変数

  • $#

    • コマンドラインの引数

  • $1 〜 $9, $0

    • 引数それぞれ。 Positional Parameters という。

  • $*

    • $0 以外のコマンドライン引数

  • $@

    • $* と類似。ただし $@ とした時、位置パラメータを評価せずにコマンドに渡すことが出来る

  • $?

    • シェルが最後に実行したコマンドの終了状態を保持している。ほとんどのコマンドは成功時には0を返す

  • $$

    • 現在のシェルのプロセス番号を保持している

  • $-

    • シェルにセットされているオプションを保持している

  • $!

    • バックグラウンドで実行された直前のプロセスのプロセス番号を保持しています

  • Reference

  • Title: Shell 特殊変数

    • URL: https://qiita.com/a_yasui/items/ec4f75b300410af8958d

文字列

文字列操作

  • 文字列の一部抜粋

    • 左から : cut -c -7

    • 右から : cut -c ``expr ${#var} + 1 - 7``-

    • 左右から : cut -c 9-13

  • 大文字・小文字変換

    • tr "a-z" "A-Z"

  • 正規表現でマッチした文字列の取り出し

    • AWK : matchstr=echo "STRING" | awk '{match($0, /PATTERN/); print substr($0, RSTART, RLENGTH)}'

    • SED : matchstr=echo "STRING" | sed 's/.*\(PATTERN\).*/\1/'

    • GREP: matchstr=echo "STRING" | grep -o 'PATTERN'

  • 特殊文字のトリミング

    • 左側のトリミング後、右側のトリミング

    • タブやスペースを取り除きたい場合は、trimming_char=printf " \t"

      string="---abc-defgh----"
      trimming_chr="-"
      
      while [ "_$string" != "_${string#[$trimming_chr]}" ]; do
          string="${string#[$trimming_chr]}"
      done
      while [ "_$string" != "_${string#[$trimming_chr]}" ]; do
          string="${string%[$trimming_chr]}"
      done
  • ファイル名・ディレクトリ名の取得

    • ファイル名取得

      • basename

      • filename="${filepath##*/}" # 左側からの最大マッチング

    • ディレクトリ名取得

      • dirname

      • dirpath="${filepath%/*}" # 右からの最小マッチング

文字列抽出

  • ${var:-word} : $varが未定義か空文字の場合は文字列wordが読み出される

  • ${var-word} : $varが未定義の場合は文字列wordが読み出される

  • ${var:=word} : $varが未定義か空文字の場合は文字列wordが読み出され、かつ$varにも代入

  • ${var=word} : $varが未定義の場合は文字列wordが読み出され、かつ$varにも代入

  • ${var:?word} : $varが未定義か空文字の場合は文字列wordが読み出され、かつエラーの扱い

  • ${var?word} : $varが未定義の場合は文字列wordが読み出され、かつエラーの扱い

  • ${var:+word} : $varが未定義でも空文字でもなければ、文字列wordが読み出される

  • ${var+word} : $varが未定義でなければ、文字列wordが読み出される

  • $#{var} : $varの文字数

  • ${var#PATTERN} : 左端からPATTERNについて最小マッチングで切り落とされて読み出される

  • ${var##PATTERN} : 左端からPATTERNについて最大マッチングで切り落とされて読み出される

  • ${var%PATTERN} : 右端からPATTERNについて最小マッチングで切り落とされて読み出される

  • ${var%%PATTERN} : 右端からPATTERNについて最大マッチングで切り落とされて読み出される

  • ${@:2} : 二つ目以降の引数を取得

  • Reference

  • Title: Shell 特殊変数

    • URL: https://qiita.com/a_yasui/items/ec4f75b300410af8958d

文字クラス

一部の環境では動作せず、使わないほうが無難

  • [[:alnum:]] : 英数字。[0-9A-Za-z]

  • [[:alpha:]] : 英字。[A-Za-z]

  • [[:digit:]] : 数字。[0-9]

  • [[:lower:]] : 英字の小文字

  • [[:upper:]] : 英字の大文字。[A-Z]

  • [[:blank:]] : スペースとタブ

  • [[:punct:]] : 記号 ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ `` { | } ~

  • [[:xdigit:]] : 16進数。[0-9A-Fa-f]

(参考) 正規表現

環境変数

IFS

  • IFS(Internal Filed Separator)

    • 設定された値が、文字の区切りとして認識される

    • ファイル等を読み込んだりする場合に利用

      • CSVファイルの場合に,を設定

メタ文字セット

BRE(基本正規表現)

  • 置換後文字列用

    • : n番目の\(\)で囲まれた範囲にマッチした文字列

    • & : マッチした文字列全体

    • \x : メタ文字&またはsed等で正規表現の始まりを住めすために用いた文字自身を指定したい場合

    • \\ : \を指定する場合

(参照) どのUNIXコマンドでも使える正規表現

ERE(拡張正規表現)

  • BREのときと比較して\をつけないなど、他多数

Others

ファイルの新規作成と追記

$ echo テスト > file
$ echo テスト >> file

ヒアドキュメント

$ cat << FIN > file
abc
def
FIN

ヒアストリング

$ A=テスト
$ sed 's/テ/ア/' <<< $A

# 以下の処理と同等
$ echo $A | sed 's/テ/ア/'

終了ステータス

$?

0で正常終了。正常ではないときは0以外の数字が終了ステータス。 テストコマンドでは正なら0、偽なら1、変な引数が指定されたら2のようにそれ以外の値が返される。

パイプで処理すると途中の処理のステータスが配列として格納される。bashで配列を扱うことができる機能

${PIPESTATUS[@]}

あるファイルがなければシェルスクリプトを終了するという処理の場合、以下のようにして書くことができる。

[ -f "/etc/passwd" ] || exit 1

引数

  • コマンド名 -- -引数のように--を指定することで、これ以降の-引数はオプションではなく、引数として解釈する

    • 実行例

      case "${arg}" in
      "--help") set -- "$@" "-h" ;;
      "--version") set -- "$@" "-V" ;;
      "--"*)
          echo "Failure: ${arg}"
          ;;
      *)
          set -- "$@" "${arg}"
          ;;
      esac
  • 引数を処理する

AWKとsed

  • awk入門 コマンドの使い方とスクリプトの書き方

  • sed - ストリームエディタ

AWK

  • 関数

    • printf

    • sprintf

    • sub

    • gsub

    • gensub

    • length(t)

    • split(s,a,fs)

    • substr(s,p,n)

    • index(s,t)

    • match(s,r)

    • tolower(s)

    • toupper(s)

  • 変数

    • ARGC

    • ARGV

    • ENVIRON

    • FILENAME

    • FNR

    • FS

    • NR

    • OFS

    • ORS

    • RS

$ seq 1 10 | xargs -n 5 > data
cat data
1 2 3 4 5
6 7 8 9 10
$ cat data | awk '{print $2,$4}'
2 4
7 9
$ cat data | awk '{a=3;print $(1+a)}'
4
9

NFは各行の列数を表す予約語

$ cat data | awk '{print $(NF-1)}'
4
9
$ cat data | awk '$4>6'
6 7 8 9 10
$ cat data | awk '$5==5'
1 2 3 4 5
$ cat data | awk '$5=="5"'
1 2 3 4 5
$ echo {a..g} | xargs -n 1
a
b
c
d
e
f
g
$ echo {a..g} | xargs -n 1 | awk 'NR>=4{print $1,$1,$1}'
d d d
e e e
f f f
g g g
$ echo {a..g} | xargs -n 1 | awk 'NR>=4{print $1,$1,$1}NR<=4{print $1,$1}'
a a
b b
c c
d d d
d d
e e e
f f f
g g g

標準入力より前処理、後処理

$ seq 1 5 | awk 'BEGIN{a=100000}{a+=1}END{print a}'
100005
$ echo {1..5} | awk 'BEGIN{a=100000}{for(i=1;i<=NF;i++){a+=$i}}END{print a}'
100015

文字列を表示

$ seq 1 3 | awk '{printf("%d円\n",$1)}'
1円
2円
3円

フォーマットを指定して表示。標準出力はしないため、一旦変数に文字列を書き出して出力

$ echo {a..z} | awk '{$9=sprintf("%s%s",$9,$9);print}'
a b c d e f g h ii j k l m n o p q r s t u v w x y z
# Once per one line
$ echo abcdfabcde | awk '{sub(/cd/,"xy",$0);print}'
abxyfabcde

# Many times per one line
$ echo abcdfabcde | awk '{gsub(/cd/,"xy",$0);print}'
abxyfabxye

# Once per line, return converted strings
$ echo abcdfabcde | awk '{$0 = gensub(/cd/,"xy",$0);print}'
abxyfabcde

3行目のみ表示

$ echo {a..e} | xargs -n 1 | awk "NR==3{print \$0}"
c

3行目から4行目まで表示

$ echo {a..e} | xargs -n 1 | awk "NR==3,NR==4{print \$0}"
c
d

cの行からdの行まで表示

$ echo {a..e} | xargs -n 1 | awk "/c/,/d/{print \$0}"
c
d

CSVファイルの第3フィールドの合計値を求める場合

$ total_size=`awk -F "," "{T=T+\\$3} END {print T}" "$filename"`

-F,のように指定すると文字列で区切る

  • (参照)

    • 【 awk 】コマンド(応用編その4)――テキストの加工とパターン処理、split関数の活用

    • 初心者用 awk 講座

sed

3行目のみ置換

$ echo {a..e} | xargs -n 1 | sed '3s/./???/'
a
b
???
d
e

2行目から最終行まで置換

$ echo {a..e} | xargs -n 1 | sed '2,$s/./???/'
a
???
???
???
???

bの行からdの行まで置換

$ echo {a..e} | xargs -n 1 | sed '/b/,/d/s/./???/'
a
???
???
???
e

指定した範囲(3行目から4行目を表示)。sedとしては入力された行をそのまま出力するのが基本動作なので-nを指定することで指定範囲の行が2行ずつ表示されることを抑えている

$ echo {a..e} | xargs -n 1 | sed -n '3,4p'        
c
d

-nを指定しない場合

$ echo {a..e} | xargs -n 1 | sed '3,4p'   
a
b
c
c
d
d
e
  • xargsは標準入力から読み込んだ文字列をしていしたコマンドに引数として渡すコマンド

    • -I@のようにオプションで文字を指定すると、別途その文字を指定した位置に引数として渡す

    • -n 1のようにいくつの引数を渡すかを指定

    • -P 5のように何並列でプロセスを立ち上げるかを指定。0を指定するとできるだけプロセスを使うように指定

jq

jq Manual (development version)

  • -rでダブルクオテーションを削除

  • []や{}で囲むと配列形式やオブジェクト形式で出力可能

  • | @csvのように渡すとCSV形式で出力可能

  • | {InstanceId, Tags:(.Tags|from_entries)} | select(.Tags.Name | contains("test"))'のようにすることでタグ指定で対象の項目を取得。Tags:の部分は出力結果の表示名。from_entriesで"Key": "auto-stop"、"Value": "yes"のような形式を"auto-stop": "yes"にまとめる

  • | lengthで長さ取得

  • | unique[]で配列に対して、重複排除。最後に[]をつけない場合、配列で結果表示

  • | fromjsonで文字列化されたJSONを復元

  • jq '.events[] | .timestamp/1000 | todate'のように秒単位にした上で| todateを使用すると時間の表示形式を整形

  • jq '.Functions[] | {FunctionName, VpcId:(.VpcConfig.VpcId//"NoVPC")}'のように//を指定すると左側がNULLのときは右側の要素に置き換える。

  • jq -r '.Reservations[] | .Instances[] | [.InstanceId, (.Tags | if (.==null) then null else ([.[] | "\(.Key)=\(.Value)"] | join(",")) end)] | @csv'のようにif () then ~ else ~ endを仕様できる。また、join()で指定文字列で文字列の配列を1つの文字列に結合

$ aws ec2 describe-instances | jq '.Reservations[] | .Instances[] | .InstanceId'
"i-0a6b646d73a79370a"
"i-096d0b925a6d835d5"
"i-07ee081ad9075e2f7"
"i-045b56c08c46b11bd"
"i-043890efa180c22a2"
"i-0048d288f55da347d"
"i-0d79cc953246cb398"
"i-0e130f4914a5938a0"
"i-079f1057910324a39"

デバッグ

  • デバッグ

    • 未定義変数参照時にエラー

      • スクリプトの冒頭に#!/bin/sh -uのように-uオプションを付与

      • set -uを宣言

  • トレース

    • スクリプトの冒頭に#!/bin/sh -xのように-xオプションを付与

  • デバッグメッセージの分離

    • FIFOパイプ

      • mkfifo /tmp/pipeしたらecho "test" > /tmp/pipeのように書き出す

      • 別端末からwhile [ 1 ];do cat /tmp/pipe;done

  • パイプの中身

    • 途中でtee /dev/stderrにリダイレクトすることやFIFOパイプを利用

Others

grepでタブ記号が入っている場合の検索

タブ記号をスペースに置き換える。[:space:]は文字クラスを表し、タブや半角スペースなどの空白文字を指す

$ cat /etc/services | grep http | tr '\t' ' ' | grep ' 80/'
http             80/udp     www www-http # World Wide Web HTTP
http             80/tcp     www www-http # World Wide Web HTTP

$  cat /etc/services | grep http | grep '[[:space:]]80/' 
http             80/udp     www www-http # World Wide Web HTTP
http             80/tcp     www www-http # World Wide Web HTTP

grepはデフォルトで基本正規表現。-Eオプションで拡張正規表現が利用でき、{5}で5回繰り返すといった表現が可能になる。

  • wcコマンド

    • -m : ロケールを考慮した文字数カウント

    • -l : 行数カウント

sed 's/./&\n/g'で文字ごとに改行できるので、行数を数えることでも計算可能。grep -o .で任意の一文字を検索して一行ごとに出力。

$ echo 123456789 | sed 's/./&\n/g'
1
2
3
4
5
6
7
8
9
$ echo 123456789 | sed 's/./&\n/g' | wc -l
10
  • unique -c : カウント

<()はプロセス置換でbashの機能

$ diff <( cd /var/log/test1 ; find ) <( cd /var/log/test2 ; find )

HTMLの編集

$ cat test.html
<!DOCTYPE html>
<!-- saved from url=(0022)https://hayashier.com/ -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
		<!-- Global site tag (gtag.js) - Google Analytics -->
		<script src="./test_files/osd.js"></script><script type="text/javascript" async="" src="./test_files/analytics.js"></script><script src="./test_files/f.txt" id="google_shimpl"></script><script async="" src="./test_files/js"></script>
		<script>
		  window.dataLayer = window.dataLayer || [];
		  function gtag(){dataLayer.push(arguments);}
		  gtag('js', new Date());
:

コメントアウト削除

$ cat test.html | sed 's/<!--/\n&\n/g' | sed 's/-->/\n&\n/g' | sed '/<!--/,/-->/d'
<!DOCTYPE html>
		<script src="./test_files/osd.js"></script><script type="text/javascript" async="" src="./test_files/analytics.js"></script><script src="./test_files/f.txt" id="google_shimpl"></script><script async="" src="./test_files/js"></script>
		<script>
		  window.dataLayer = window.dataLayer || [];
		  function gtag(){dataLayer.push(arguments);}
		  gtag('js', new Date());

タグ削除。以下、実体参照が正しく使用されている場合

$ cat test.html | sed 's/<!--/\n&\n/g' | sed 's/-->/\n&\n/g' | sed '/<!--/,/-->/d' | sed 's;<[^<]*>;;g' | awk 'NF!=0'
		  window.dataLayer = window.dataLayer || [];
		  function gtag(){dataLayer.push(arguments);}
		  gtag('js', new Date());

ファイルの同期

$ rsync -av ~/ --exclude='.Trash' 192.168.5.1:~/home/hayashier/

sedでまとめて置換

sedで置換するルールを予めファイルに列挙しておいてまとめて置換することができる

$ cat /var/log/apach2/access.log* | ./month.sed | tail -n 1
#!/bin/sed -f

s/Jan/01/
s/Feb/02/
s/Mar/03/
s/Apr/04/
s/Mar/05/
s/Jun/06/
s/Jul/07/
s/Aug/08/
s/Sep/09/
s/Oct/10/
s/Nov/11/
s/Dec/12/

sedで部分一致箇所の抜粋

()で囲っておくと、\1のようにして取り出せる

$ echo 'A B C [D] "E" F G " "H" "I"' | sed 's/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] "\(.*\)" \(.*\) \(.*\) "\(.*\)" "\(.*\)"$/\1|\2|\3|\4|\5|\6|\7|\8|\9/'
A|B|C|D|E|F G|"|H|I

Apacheのアクセスログの分析例

  • 改行やスペースを除く、文字でないバイナリを除去する

  • データ内の区切り文字以外の"や区切り文字を含んでいてっもフィールドを区切れるようにバックスラッシュは\\と記録されているので、これを%5Cにエンコーディング

  • "は\"と記録されているので、これを%22にエンコーディング

#!/bin/bash -xv

B='\(.*\)'
D='"\(.*\)"'
P='\[\(.*\)\]'
STR='\1\x0\2\x0\3\x0\4\x0\5\x0\6\x0\7\x0\8\x0\9\x0'

sed 's;\\\\;%5C;g' < /dev/stdin             |
sed 's;\\";%22;g'                           |
sed "s/^$B $B $B $P $D $B $B $D $D\$/$STR/" |
sed 's/_/\\_/g'                             |
sed 's/ /_/g'                               |
sed 's/\x0\x0/\x0_\x0/g'                    |
sed 's/\x0\x0/\x0_\x0/g'                    |
tr '\000' ' '                               |
sed 's/ $//'

IPアドレスのソート

$ cat ip
192.168.1.2
10.245.0.1
10.95.20.1
40.1.212.4
203.113.10.1
$ sort -t . -k1,1n -k2,2n -k3,3n -k4,4n ip
10.95.20.1
10.245.0.1
40.1.212.4
192.168.1.2
203.113.10.1

以下の書籍等を参考にさせていただき、自分用の備忘録にまとめました。

  • 覚えて便利 いますぐ使える! シェルスクリプトシンプルレシピ54

  • コンパチブル・シェルスクリプティング(第5版)

  • シェルプログラミング実用テクニック

  • GihyoShellBookSamples

  • Bash Reference Manual

  • とほほのBash入門 !

Others

  • setコマンドの使い方

    • 【 set 】コマンド――シェルの設定を確認、変更する

Previousソフトウェアテスト 入門Nextサービス障害が発生した場合の対応方法

Last updated 1 month ago