Open46

シェル

環境変数

  • シェルからコマンドを実行すると、forkシステムコールよって現在のシェルから子プロセスが作られる。
  • 親プロセスから子プロセスに引き継がれる情報の一つに環境変数がある(普通のシェル変数はそのプロセスでのみ有効)
  • 現在設定されている環境変数の一覧はprintenvコマンドで確認できる
  • 環境変数に自分で定義した変数を設定するにはexportコマンドを使用する
  • 環境変数は慣習として大文字を使う
  • export $変数名で通常の変数を環境変数に設定できる

printenv/env/set/export

説明 printenv env set export
概要 環境変数の一覧を表示 環境変数の一覧を表示 シェル変数と環境変数の一覧を表示 環境変数の一覧を表示
環境変数対応
シェル変数変数対応 × × ×

代表的な環境変数

環境変数 説明
HISTFILE コマンドの履歴保存ファイルパス
HISTSIZE 現在のシェルでのコマンド履歴の保存数
HISTFILESIZE コマンド履歴保存ファイルへの履歴保存数
HOSTNAME ホスト名
PWD カレントディレクトリのパス
USER ログインしているユーザ

変数

  • 「変数名=値」とした場合、変数の値は文字列として扱われる(''や””で囲む必要はない)
  • スペースやタブを含む場合は文字列を囲む
スペースやタブを含む場合はシングルクオートで文字列で囲む
$ file='Document files'
  • 変数にから文字列も代入できる
変数に空文字列を代入
$ empty1=
$ empty1=''

変数の参照

  • 変数の値を参照するには、変数の前に$をつける
変数の参照
$ directory=/home
$ cd $directory
pwd
/home
値を設定していない変数は空文字列になる
$ echo For Whom the $x Tolls
For Whom the Tolls
  • 変数名と文字列の区切りを明示するために{}で囲む
変数名と文字列の区切りを{}で明示する
item=pen
$ echo I have many ${item}s
I have many pens

declareによる変数宣言

  • bashでは特に宣言せずに変数を使用できるが、これを明示的に宣言する場合はdeclareを使う
  • 特に指定しない場合は、型は文字列となる
オプション 属性
-r 変数を読み取り専用として扱う
-i 変数を整数として扱う
-a 変数を配列として扱う
-A 変数を連想配列として扱う

shiftコマンド

  • 位置パタメータの番号をずらすためのコマンド
  • 主に解析済みの位置パラメータの値を捨てるために使用する
#!/bin/bash
echo "$1, $2, $3, $4, $5"
shift
echo "$1, $2, $3, $4, $5"
$ bash shift.sh aaa bbb ccc ddd eee
aaa, bbb, ccc, ddd, eee
bbb, ccc, ddd, eee, 

数値の合計値を計算する

#!/bin/bash
# シェルスクリプトのファイル名を取り出して変数に代入している
# $0変数にはコマンドとして指定した名前が入っている
# 値の上書きしてしまう間違いを防ぐための読み取り専用を宣言している
readonly SCRIPT_NAME=${0##*/}

result=0

# $@: 全ての引数
for number in "$@"
do
    # 先頭にマイナス符号が付いた数字の連続のみを許容する
    # =~: 拡張正規表現にマッチすれば真
    # +: 1回以上の繰り返し
    if [[ ! $number =~ ^-?[0-9]+$ ]]; then
        # 出力する文字列がechoコマンドのオプションとして解釈される可能性を考慮してprintfを使う
        printf '%s\n' "$SCRIPT_NAME: '$number' non-integer number" 1>&2
        # エラーとみなしたときに終了ステータスとして1を返して、シェルスクリプト自体を終了する
        exit 1
    fi
    # 算術式評価
    ((result+=number))
done

printf '%s\n' "$result"

指定したユーザの情報を出力する

#!/bin/bash
# シェルスクリプトのファイル名を取り出して変数に代入している
# $0変数にはコマンドとして指定した名前が入っている
# 値の上書きしてしまう間違いを防ぐための読み取り専用を宣言している
readonly SCRIPT_NAME=${0##*/}

user=$1

# -z: 文字列が空の場合に真
if [[ -z $user ]]; then
    printf '%s\n' "$SCRIPT_NAME: missing userbane" 1>&2
    exit 1
fi

cat /etc/passwd \
| grep -E "^$user:" \
| {
    #IFSの値を:に設定しているので、readコマンドは読み取った行を:で区切って,
    #先頭から順にusername, password, といった変数に値を代入
    IFS=: read -r username password userid groupid \
    comment homedirectory loginshell
    
    # readコマンドは1行分の文字列が読み込めた場合は終了ステータス0を返す
    # 一方、EOF (End Of File) が到達した場合は終了ステータス1を返す
    # $?は上のコマンドの戻り値を返す
    # -ne: 数値が等しくない
    if [[ $? -ne 0 ]]; then
        printf '%s\n' "$SCRIPT_NAME: $user not found" 1>&2
        # エラー種別によって終了ステータスを変えると呼び出し側でエラーの種類が区別できるようになる
        exit 2
    fi
    cat <<END
username = $username
userid = $userid
password = $password
groupid = $groupid
comment = $comment
homedirectory = $homedirectory
loginshell = $loginshell
END
}

圧縮されたファイルを展開する

#!/bin/bash

# 名前
#  extract.sh - 圧縮されたファイルを展開する
# 書式
#   extract.sh FILE...
# 説明
#   指定した圧縮ファイルを展開する。
#   対応しているファイル形式は次のとおり。
#       .gz
#       .bz2
#       .xz
#       .tar
#       .tar.gz, .tgz
#       .tar.bz2, .tbz2
#       .tar.xz, .txz
#       .zip

readonly SCRIPT_NAME=${0##*/}

extract_one()
{
    local file="$1"
    
    # 引数が指定されていない場合はエラー
    if [[ -z "$file" ]]; then
        echo "$SCRIPT_NAME: missing file operand" 1>&2
        # エラーと判断したときはreturnコマンドで関数を終了
        # 一つのファイルでエラーが発生したとしても残りのファイルの展開処理を続けたいので
        # exitを使わずにreturnコマンドを使う
        return 1
    fi
    
    # ファイルが存在しない場合はエラー
    if [[ ! -f "$file" ]]; then
        echo "$SCRIPT_NAME: $file: No such file or directory" 1>&2
        return 2
    fi
    
    # .以降を取り除いた文字列を変数に代入
    # ex. test.1.gz -> test.1
    local base="${file##*/}"
    
    case "$file" in
        *.tar.gz | *.tgz)
            tar -xzf "$file"
        ;;
        *.tar.bz2 | *.tbz2)
            tar -xjf "$file"
        ;;
        *.tar.xz | *.txz)
            tar -xJf "$file"
        ;;
        *.tar)
            tar -xf "$file"
        ;;
        *.gz)
            # --以降はオプションではないとみなされる
            gzip -dc -- "$file" > "$base"
        ;;
        *.bz2)
            bzip2 dc -- "$file" > "$base"
        ;;
        *.xz)
            xz -dc -- "$file" > "$base"
        ;;
        *.zip)
            unzip -o "$file"
        ;;
        # *は上記のいずれかにもマッチしない
        *)
            printf '%s\n' "$SCRIPT_NAME: $file: Unknown file type" 1>&2
            return 3
        ;;
    esac
}

if [[ $# -le 0 ]]; then
    echo "$SCRIPT_NAME: missing file operand" 1>&2
    exit 1
fi

result=0
for i in "$@";
do
    # || は左辺が異常終了した場合に右辺を実行する
    # 終了ステータスが0以外の場合は、結果を変数resultに代入
    # すべてのファイルに対して処理が成功した場合は0
    # 一つでも失敗した場合は1
    extract_one "$i" || result=$?
done

exit "$result"

新しいファイルとその途中のディレクトリを作成する

#!/bin/bash

# 名前
# dtouch.sh - 新しいファイルとその途中のディレクトリを作成する
# 書式
#   dtouch.sh FILE...
# 説明
#   指定したパスのファイルを新規に作成する。
#   途中のディレクトリが存在していない場合はそれも作成する

readonly SCRIPT_NAME=${0##*/}

dtouch_one()
{
    local path=$1
    
    if [[ -z $path ]]; then
        printf '%s\n' "$SCRIPT_NAME: missing file operand" 1>&2
        return 1
    fi
    
    local dir=
    # ディレクトリを取得
    # work/hoge/fuga/README.txt -> work/hoge/fuga
    if [[ $path == */* ]]; then
        dir=${path%/*}
    fi
    
    # ディレクトリを作成
    # -n: 文字列長が0より大ならば真
    # -d: ディレクトリならば真
    if [[ -n $dir && ! -d $dir ]]; then
        mkdir -p $dir || return 1
    fi
    
    # ファイルを作成
    # -e: 指定したパスが存在していれば真
    if [[ ! -e $path ]]; then
        touch -- "$path"
    fi
}

if [[ $# -le 0 ]]; then
    printf '%s\n' "$SCRIPT_NAME: missing file operand" 1>&2
    exit 1
fi

result=0
# $@: 全てのパラメータ
for i in "$@";
do
    dtouch_one "$i" || result=$?
done

exit $result

標準入出力

名前 内容
標準入力(stdin) 標準的な入力元。通常はキーボード
標準出力(stdout) 標準的な出力先。通常は端末ディスプレイ
標準入力(stdin) 標準的なエラー出力先。通常は端末ディスプレイ

リダイレクト

  • コマンドを実行するときに標準入出力の入力元、出力先を置き換えるシェルの機能
  • 多くのコマンドは結果を標準出力に出力するので、リダイレクトによって出力結果を指定したファイルに保存できる
リダイレクト 説明
> 出力のリダイレクト
>> 出力の追記
< ファイルの内容を標準入力に送る
<< 終了文字が現れるまでコマンドの標準入力に入力を続ける
<<< 指定した文字列を標準入力に送る

noclobberと出力のリダイレクト

  • >によって出力をリダイレクトした場合、リダイレクト先のファイルが存在しない場合はファイルが新規に作成される
  • 既にファイルが存在する場合、ファイルの内容は失われて上書きされてしまう
  • noclobberの設定を有効にすると、>によるリダイレクトで既に存在するファイルをしてもエラーとなり、上書きされない
$ set -o noclobber
$ touch result.txt
$ ps > result.txt
  • >|は、noclobberの設定を無視して強制的に上書き保存でリダイレクトするための記号

リダイレクトと追記

  • >による上書きではなく、元のファイルへ追記したい場合は>>を使用する
$ echo line1 >> echo.txt
$ echo line2 >> echo.txt
$ echo line3 >> echo.txt
$ cat echo.txt
line1
line2
line3

標準入出力とファイルディスクリプタ番号

名前 ファイルディスクリプタの番号
標準入力 0
標準出力 1
標準エラー出力 2

/dev/null

  • /dev/nullから読み込んでも何もデータは返されない
  • /dev/nullにデータを書き込んでもどこにも書き込まれることなくデータは消える

コマンドのグループ化

  • 複数のコマンドを{}で囲んで一つのコマンドとしてまとめる機能のこと
#!/bin/bash
{
   date
   echo '/usr'
   ls /usr
} > result.txt

trapコマンド

  • 現在のプロセスに対して送られたシグナルを補足するためのコマンド
sleep.sh
#!/bin/bash
trap 'echo receive INT signal' INT

echo start
sleep 10
echo end
$ ./sleep.sh
start
^Creceive INT signal # Ctrl+Cが押されると、メッセージが表示される
end # スクリプトが最後まで実行されている
  • シェルスクリプトのプロセスに対してINTというシグナルが送られて、trapコマンドでそのINTシグナルを補足している
  • シグナルを受け取った後にそのスクリプトの処理を終了したい場合は、明示的にexitコマンドを記述する
  • 現在のシステムで使用できるシグナル一覧は、kill -lコマンドで確認できる
シグナル 番号 解説
HUP 1 プロセスの再起動を通知する
INT 2 プロセスの割り込みを通知する(Ctrl+C)
QUIT 3 プロセスの終了を通知し、コアダンプファイルを作成する(Ctrl+\ )
TERM 15 プロセスの終了を通知する

evalコマンド

  • 引数で指定した文字列をシェルのコマンドラインとして解釈して実行するためのコマンド

ブレース展開

  • 複数の文字列を一度に指定する記法
$ echo file-{1001,1002,old,new,}.txt
# -> file-1001.txt file-1002.txt file-old.txt file-new.txt file-.txt

チルダ展開

  • ユーザのホームディレクトリをを指定するための記法
$ ls ~

パラメータ展開

:-による展開

  • 値を省略したときのデフォルト値を返す
#!/bin/bash
echo "${name:-John}"  # -> John
name=Jack
echo "${name:-John}"  # -> Jack 
引数のデフォルト値を設定する
$ ls "${1:-/}"
  • :を省略して-だけにすると、空文字列かどうかにかかわらず変数が設定されているかだけを判別する
#!/bin/bash
name= # から文字列を設定
echo ${name-John} # 設定されている空文字が展開される
name=Jack
echo ${name-John} # -> Jack
unset name # 変数を未定義状態にする
echo ${name-John} # -> John
  • UNIX系OSのファイルシステムは「ファイル名->ファイル実体」という対応付けでなく、間にinodeを挟んだ「ファイル名 -> inode -> ファイル実体」という対応付け
  • ls -iコマンドでinode番号を確認できる
  • inodeは、ファイル種類の情報、ファイルのアクセス権限やタイムスタンプ、ディスク上のどこに実データが存在するかというメタデータを保持するデータ構造

ファイル編集

  • ファイルを編集してもinodeは同じ
$ echo hoge > hoge.txt
$ ls -i hoge.txt
47580068 hoge.txt
$ echo fuga >| hoge.txt
$ ls -i hoge.txt
47580068 hoge.txt

ファイルの置き換え

  • mvするとinodeは変わる
$ echo hoge > hoge.txt
$ ls -i hoge.txt
9871798 hoge.txt

$ echo fuga > fuga.txt
$ ls -i fuga.txt
9872073 fuga.txt

$ mv fuga.txt hoge.txt
$ ls -i hoge.txt
9872073 hoge.txt

シェルスクリプトでリダイレクトによる上書き防止

  • set -Cもしくはset -o noclobberでリダイレクトによる上書き禁止
  • リダイレクト演算子>|にすればオプション関係なく上書き可能

Check exit code directly with e.g. 'if mycmd;', not indirectly with $?

https://github.com/koalaman/shellcheck/issues/1167

Problematic code:

make mytarget

if [ $? -ne 0 ]
then
  echo "Build failed"
fi.

Correct code:

if ! make mytarget;
then
  echo "Build failed"
fi

Rationale:

  • Running a command and then checking its exit status $? against 0 is redundant.
  • Since the command and its status test are decoupled, inserting an innocent command like echo "make finished" after make will cause the if statement to silently start comparing echo's status instead.
  • Scripts that run or are called with set -e aka errexit will exit immediately if the command fails, even though they're followed by a clause that handles failure.

パイプ終了のステータス

  • $? だと、最後に終了したコマンドの終了コード0(正常終了)が返ってくる
  • ${PIPESTATUS[@]}だとPIPESTATUSという配列変数で各コマンドの終了ステータスを取得できる
  • set -o pipefailを有効にすると、Pipeでエラーがあった時のエラーコードを返す
#!/bin/bash

foo() { exit 11; }
bar() { exit 12; }
baz() { exit 13; }

foo | bar | baz
echo $? # => 13

foo | bar | baz
echo "${PIPESTATUS[@]}" # => 11 12 13

set -o pipefail
foo | bar | baz
echo $? # => 13

bashにおける各クォートの判定

  • Double quote: 変数展開して出力
  • Single quote: そのまま文字を出力
  • Back quote: バッククォートで囲った内容はコマンドとして認識する、ただしわかりにくいので推奨されていない
    ※Shift+@でバッククォートで入力でき
# This is preferred:
var="$(command "$(command1)")"
# This is not:
var="`command \`command1\``
#!/bin/bash

string='Hello World!!'
echo "Double quote: ${string}" # => Double quote: Hello World!!
echo 'Single quote: ${string}'  # => Single quote: ${string}
echo `Back quote: ${string}`  # => /Users/example/Untitled-1.sh: line 6: Back: command not found

ブレース展開

$ echo abc{,.bak}
abc abc.bak

$ mkdir test{1..9}
$ ls
mkdir test1 test2 test3 test4 test5 test6 test7 test8 test9

$ for i in `ls`; do mv $i{,.png}; done
$ ls
test1.png test3.png test5.png test7.png test9.png
test2.png test4.png test6.png test8.png

ログインシェル

  • 最初に起動するシェル(ログイン時)

インタラクティブシェル

  • ログインシェルから起動するシェル(zshやbashと直打ちして起動した状態)

プロセス置換(Proceess Substitution)

  • コマンドの結果をファイルのように扱うことができる機能
  • 一時ファイルを作らなくて済む
  • <(...) : コマンドの実行結果を一時的なファイルとして与える
  • >(...) : 一時ファイルへの出力を指定したコマンドの標準入力として処理する
diff+プロセス置換の例
$ haseyuy@mac ~ % hostname
mac.local
$ haseyuy@mac ~ % hostname | tr a-z A-Z
MAC.LOCAL
$ haseyuy@mac ~ % diff <(hostname) <(hostname | tr a-z A-Z)
1c1
< mac.local
---
> MAC.LOCAL

teeコマンド

  • 標準入力と標準出力とファイルに書き出すコマンド

標準出力をファイルに保存する

  • パイプを使用して、teeコマンドに標準入力として渡す
$ echo "Hello World" | tee a.txt
Hello World
$ haseyuy@mac ~ % cat a.txt

ファイルに追記する

  • -aオプションを付けると指定したファイルが存在する場合、上書きでなく追記を行う
$ echo "hello world" | tee -a a.txt
hello world
$ cat a.txt
Hello World
hello world

標準エラー出力もファイルに保存する

  • パイプを使用した場合、標準エラー出力は指定したコマンドに渡されない
$ ping -c 4 host999 | tee pinglog.txt
ping: cannot resolve host999: Unknown host
$ cat pinglog.txt
  • 標準エラー出力も保存したい場合は、2>&1を指定することで、標準出力と標準エラー出力の両方をteeコマンドの標準入力に渡すことができる
$ ping -c 4 host999 2>&1 | tee pinglog.txt
ping: cannot resolve host999: Unknown host
$ cat pinglog.txt
ping: cannot resolve host999: Unknown hos

diffコマンド

unified形式で差分表示(-u, --unified)

context形式形式で差分表示(-c, --context)

差分があるかどうか(-q, --brief)

横に並べて表示(-y)

大文字と小文字の違いを無視(-i, --ignore-case)

空白を無視(-w, --ignore-all-space)

trコマンド

  • ファイル内の文字を変換・削除するコマンド
  • trコマンドは変換対象を標準入力からのみ受け付けるので、|や<を使用して標準入力にファイルの内容を渡す
  • 例 : tr -s [:spage:] < file
  • 例 : cat file | tr -s " "
オプション 説明
-d 文字列1で指定した文字を削除
-s 文字列1で指定した文字が連続した場合、1文字に置き換える
文字クラス 説明
[:alpha:] 英字
[:lower:] 英小文字(a-z)
[:upper:] 英大文字(A-Z)
[:upper:] 数字(0-9)
[:upper:] 英数字
[:space:] スペース

timeoutコマンド

  • 制限時間を付けて指定のコマンドを実行するコマンド
  • 制限時間付きで実行させ、制限時間を超えたらタイムアウトさせたい時に使う
  • 第一引数に制限時間、第二引数に実行するコマンドをわたす
haseyuy@mac ~ % timeout 3 ping localhost
PING localhost (127.0.0.1): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
haseyuy@mac ~ % timeout 3 sleep 5
haseyuy@mac ~ % echo $?
124  # if COMMAND times out, and --preserve-status is not specified

haseyuy@mac ~ % timeout 5 sleep 3
haseyuy@mac ~ % echo $?
0

netstatコマンド

  • 実行コンピュータが、どのコンピュータとどんなプロトコルを使って何番ポートで通信しているかがわかる
  • 各行が一つのコネクションで、通信先が同じでもポートやアドレスが異なれば別コネクション
オプション 説明
-n ポート番号をサービス名に変換しない(:httpとせず:80を表示する)
-l LISTENポートのみ表示する
-t TCPを表示する
-u UDPを表示する

TCP状態遷移

  • どちらが先に要求を送信したのかによって処理内容が変化する

状態一覧

LISTEN

  • パッシブ・オープンで待ち受け状態になっていることを表す

SYN-SENT

  • クライントがサーバにSYNを送信したが、まだそれに対する応答ACKを受け取っていない状態

SYN-RECV

  • サーバはSYNを受信したので、SYN+ACKをクライアントに返し、SYN-RECV状態に移行する

ESTABLISHED

  • クライアントは、サーバからSYN+ACKを受信し、ACKをサーバに返すと、ESTABLISHED状態になる
  • サーバはクライアントからのACKを受信すると、ESTABLISHED状態になる
  • これでデータを双方向に送受信する準備ができる

ヒアドキュメント

  • ヒアドキュメントの内容が標準入力としてコマンドに渡される
  • 複数行を出力するときに、何度もechoコマンドを使わなくてよいので楽
#!/bin/bash
cat << END
Usage: ./Untitled-1.sh [OPTION]... [FILE]...
-a   print the file names of all files in the current directory
END

ヒアストリング

  • ヒアドキュメントを1行にした書き方
  • <<<の右側に書いた要素に対してはパラメータ展開、コマンド置換、算術式展開、ブレース展開、チルダ展開は行われる
$ str=abc
$ tr b B <<< "$str"
aBc

ヒアドキュメントで直接ファイルに書き込む

#!/bin/bash
cat << EOT >> out.txt
Usage: ./Untitled-1.sh [OPTION]... [FILE]...
-a   print the file names of all files in the current directory
EOT

sed

  • 入力ストリームに対してテキスト変換などの処理を行なって結果を出力する
指定方法 説明
数字 指定した行番号の行
/正規表現/ 正規業限にマッチする行
$ 最終行
$ sed s/検索パターン/置換後の文字列/フラグ
  • sコマンドは文字列を置換するコマンド
  • sコマンドの末尾に指定するフラグは置換の動作を変更するための記号で、主にgiが使われる
  • gは検索パターンにマッチしたすべての箇所を置換するフラグ、このフラグがないと各行の中で検索パターンにマッチした最初の箇所だけが置換される
  • iはアルファベットの大文字小文字の違いを無視するフラグ

/を検索するとき

  • 検索パターンに置換後文字列に/を含めたい場合は\/とエスケーケープする必要がある

iオプション

  • 入力ファイルを直接編集して上書き保存するためのオプション
  • -iのすぐ後に文字列を指定すると、変更する前に元のファイルのバックアップが作成される
バックアップファイルを作る
$ echo test > test.txt
$ sed -i.bak 's/test/hello/g' test.txt
$ ls 
test.txt test.txt.bak
$ cat test.txt
hello
$ cat test.txt.bak
test

fオプション

  • 編集コマンドを記述したファイルを指定する
$ cat test.txt
ping1
ping2
#ping3
$ cat editfile
s/ping/hoge/g
/^#/d
$ sed -f editfile test.txt
hoge1
hoge2

fオプション

  • 編集コマンドを記述したファイルを指定する

ヒアドキュメントで直接ファイルに書き込む

#!/bin/bash
cat << EOT >> out.txt
Usage: ./Untitled-1.sh [OPTION]... [FILE]...
-a   print the file names of all files in the current directory
EOT
#!/usr/bin/env bash

set -Eeuo pipefail
trap cleanup SIGINT SIGTERM ERR EXIT

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)

usage() {
  cat << EOF # remove the space between << and EOF, this is due to web plugin issue
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...]

Script description here.

Available options:

-h, --help      Print this help and exit
-v, --verbose   Print script debug info
-f, --flag      Some flag description
-p, --param     Some param description
EOF
  exit
}

cleanup() {
  trap - SIGINT SIGTERM ERR EXIT
  # script cleanup here
}

setup_colors() {
  if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
    NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
  else
    NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
  fi
}

msg() {
  echo >&2 -e "${1-}"
}

die() {
  local msg=$1
  local code=${2-1} # default exit status 1
  msg "$msg"
  exit "$code"
}

parse_params() {
  # default values of variables set from params
  flag=0
  param=''

  while :; do
    case "${1-}" in
    -h | --help) usage ;;
    -v | --verbose) set -x ;;
    --no-color) NO_COLOR=1 ;;
    -f | --flag) flag=1 ;; # example flag
    -p | --param) # example named parameter
      param="${2-}"
      shift
      ;;
    -?*) die "Unknown option: $1" ;;
    *) break ;;
    esac
    shift
  done

  args=("$@")

  # check required params and arguments
  [[ -z "${param-}" ]] && die "Missing required parameter: param"
  [[ ${#args[@]} -eq 0 ]] && die "Missing script arguments"

  return 0
}

parse_params "$@"
setup_colors

# script logic here

msg "${RED}Read parameters:${NOFORMAT}"
msg "- flag: ${flag}"
msg "- param: ${param}"
msg "- arguments: ${args[*]-}"

プロセス周り

プロセスの複製

  • システムコール fork() を呼び出した親プロセスは、子プロセスを生成する
  • fork()の戻り値として親プロセス側には子プロセスのIDが渡され、子プロセス側には0が渡されるので、自分が親か子かを判別する

子プロセスの置き換え

  • 子プロセスはexecシステムコールを呼び出し、自分を別のプログラムに置き換える
  • プロセスIDは維持される

プロセスツリー

  • プログラムの実行はfork()->exec()を利用して、次々と子プロセスを生成する
  • 一番最初のプロセスはカーネル自身がinitというプロセスを生成し、全ての親になる

プロセスの実行優先度

  • プロセスにはnice値という実行優先度が設定されている
  • nice値-20 から 19 まであり、低い ほど優先度は高い
  • デフォルト値は** 0 **
  • niceコマンドにてプロセスの実行優先度を設定する
  • niceコマンドを単体で実行した場合は ** 10 **
  • ** niceとreniceでは指定方法が違うので注意する **
testプログラムを最も高い優先度に設定
$ nice --20 test 
$ nice -n -20 test  # niceには-nオプションがある
  • すでに起動しているプロセスのnice値を変更するにはreniceコマンドを利用する
  • reniceは-pをつけるか否か
PID500のnice値を変更
$ renice -20 -p 500
$ renice -20 500
  • プロセスのnice値を表示するコマンドとしては、psコマンドやtopコマンドがある
ps l
$ ps -l # NIの部分がnice値
  UID   PID  PPID        F CPU PRI NI       SZ    RSS WCHAN     S             ADDR TTY           TIME CMD
  501  8195  8194     4006   0  31  0 408956768   1056 -      S+                  0 ttys000    0:00.14 -zsh

フォアグランドとバックグラウンド

  • コマンドライン末尾に&を付けて実行
  • jobsコマンドでバックグランドで動作している、または一時停止中のジョブを表示
$ ping localhost > ping.txt &
[1] 14979
$ jobs
[1]+  実行中               ping localhost > ping.txt &

コマンド

psコマンド

  • lオプション, -lオプションでPIDやnice値なども併せて表示する
  • ps axf で動作中のプロセスの親子関係をツリー状に表示
  • ps -ef または ps ax で全てのプロセスを表示する
オプション 説明
a 他のユーザのプロセスも表示
f プロセスの親子関係をツリー状で表示
u プロセスの実行ユーザ名も表示
-p p PID
-e 全てのプロセスを表示
-f 完全なフォーマットでプロセスを表示
x 制御端末の無いデーモン等のプロセスも表示
-l | l 親プロセスのPIDやnice値も併せて表示

pgrepコマンド

  • 実行中のプロセスから特定の名前を持つプロセスIDを検索
  • -Uオプションで指定した実効ユーザによるプロセスを対象とする
  • -uオプションで指定した実ユーザによるプロセスを対象とする

実効ユーザと実ユーザの違い

kill/killall/pkillコマンド

  • シグナルは kill または killall コマンドでプロセスを送る
  • シグナルが指定されていない場合、TERMシグナル(クリーンアップして終了)を送る
  • TSTPは一時停止の時と Ctrl + ZのZと関連づける
  • SIGINTのINTはinterruptの略
  • HUPシグナルは、プログラムの設定ファイルを変更した後その設定ファイルをプロセスに再度読み込ませて設定を反映させる為に用いられる
シグナル名 シグナル番号 動作
HUP(SIGHUP) 1 ハングアップ(端末の切断による終了)
INT(SIGINT) 2 ハングアップ(割り込みよる終了)(Ctrl+C押下と同様)
KILL(SIGKILL) 9 クリーンアップせずに終了(強制終了)
TERM(SIGTERM) 15 クリーンアップして終了(デフォルト)
CONT(SIGCONT) 18 一時停止のプロセス再開
STOP(SIGSTOP) 19 一時停止
TSTP(SIGTSTP) 20 端末から入力された一時停止(Ctrl+Z押下と同様)

nohupコマンド

  • ログアウト後もジョブを実行させたい場合は nohup コマンドを使用する

watchコマンド

  • 指定したコマンドを一定時間ごとに繰り返し実行し、結果を表示するコマンド
  • デフォルトは2秒ごとにコマンドを実行
  • -nオプションで実行間隔を指定

fgコマンド

  • ジョブ番号を引数に指定しない場合は、カレントジョブ(直前にバックグラウンドで実行されたジョブ、または直前に一時停止されたジョブ)がフォアグラウンドに変更される

alisasを無視する方法

  • コマンドの前で\エスケープする
$ \cp a b

パーティション形式

MBR(Master Boot Record)

  • パーティション数は、4つのパーティションを作成できる
  • 2TBまでサポート

GPT(GUID Partition Table)

  • 最大128個までパーティションを作れる
  • 2TB以上のディスクサイズの管理可能
  • マザーボードがUEFIに対応している必要がある

findコマンド

  • 指定したディレクトリ以下からファイルやディレクトリを検索するコマンド

-name ファイル名

-atime 日数

-mtime 日数

-type ファイルの種類

カレントリディレクトリは以下の通常ファイルを検索
$ find . -type f
カレントリディレクトリは以下のディレクトリを検索
$ find . -type d

-user ユーザ名

/var配下でapacheユーザが所有するファイルを検索
$ find /var -user apache

-mtime 日数

$ find ./ -mtine -3    # 2日以内に更新されたファイルを検索
$ find ./ -mtine 3   # 3日前に更新されたファイルを検索
$ find ./ -mtine +3   # 3日より前に更新されたファイルを検索

-maxdepth 階層数 / -mindepth 階層数

  • 指定した階層から下を検索

-print

  • 検索結果で改行で区切って表示
検索結果を改行で区切って表示
$ find /etc -name httpd -print
/etc/logrotate.d/httpd
/etc/httpd
検索結果をNULL文字で区切って表示
 $ find /etc -name httpd -print0
/etc/logrotate.d/httpd/etc/httpd

-print0

  • 検索結果をNULL文字で区切って表示

-exec コマンド名

  • 検索結果のファイルに対してコマンドを実行

名前付きパイプ

  • システム上の複数プロセスが双方向にデータを交換できる通信チャネル
  • mkfifoコマンドで名前付きパイプファイルが作成できる
$ mkfifo np
$ ls -l np
prw-r--r-- 1 root root 0  123 19:25 np #ファイルタイプがpとなる
# 別のターミナルでファイルに書き込むと、ブロックする
$ echo hello > np
# 元のターミナルでこのファイルをcatする
$ cat np
hello

locateコマンド

  • 検索用データベースを使用して高速でファイル検索を行うコマンド
  • 設定ファイルは/etc/updatadb.conf
  • updatedbコマンドで利用するファイルデータベースを更新する

whichコマンド

  • 指定したコマンドが格納されている場所を検索し、絶対パスで表示
  • 検索するディレクトリは環境変数PATHが通っているディレクトリ
$ which mkdir
/usr/bin/mkdir

whereisコマンド

  • コマンドが格納された場所を検索し、絶対パスで表示。環境変数のPATHの値は使用しない
  • マニュアルが格納されている場所も表示する
$ whereis mkdir
mkdir: /usr/bin/mkdir /usr/share/man/man1/mkdir.1.gz /usr/share/man/man1p/mkdir.1p.gz /usr/share/man/man2/mkdir.2.gz /usr/share/man/man3p/mkdir.3p.gz

typeコマンド

  • そのコマンドがビルトインか、実行ファイルなのか表示
$ type cat 
cat はハッシュされています (/usr/bin/cat) # コマンドの場所へ高速アクセスするために、パスをキャッシュされている

$ type echo
echo はシェル組み込み関数です

$ type ll
ll は `ls -l --color=auto' のエイリアスです

ファイル操作系コマンド

lsコマンド

オプション 説明
-a 隠しファイルを含む全てのファイルとディレクトリを表示
-A 隠しファイルを含む全てのファイルとディレクトリを表示、但しカレントディレクトリと親ディレクトリは表示しない
-F ファイルタイプの表示
-i inode番号を付けて表示

mkdirコマンド

  • ディレクトリを作成するコマンド
  • -m アクセス権で指定したアクセス権でディレクトリを作成(permissionなのでpだと思うが、modeのmであることに注意)
  • -pで引数に階層のあるディレクトリを指定した場合、存在しない中間ディレクトリも同時に作成(--parentsのp)

rmdirコマンド

  • 空のディレクトリのみ削除することができるコマンド
  • -p ディレクトリ名は、階層を指定した場合、削除するディレクトリの親ディレクトリを同時に削除する

ddコマンド

  • データをコピーする
  • デバイスを引数にとってデバイス間のコピー(バックアップ)や、フロッピー/CD-ROMのイメージをそのままコピーする事もできる
  • count以外は全て2文字のオプション
オプション 説明
if=入力ファイル 入力側ファイル(デバイス)を指定
of=出力ファイル 出力側ファイル(デバイス)を指定
bs=バイト数 入出力のブロックサイズを指定、デフォルトは512バイト
count=回数 入力ファイルから出力ファイルへブロックをコピーする回数を指定
5MBのファイルを作成する
$ dd if=/dev/zero of=testfile bs=1M count=5
5+0 レコード入力
5+0 レコード出力
5242880 bytes (5.2 MB, 5.0 MiB) copied, 0.00349318 s, 1.5 GB/s
$ ls -l -h
-rw-r--r--  1 root root 5.0M  125 23:19 testfile

statコマンド

  • ファイルの属性値を表示する
  • Inode数も表示する
$ stat testsh
  File: test.sh
  Size: 593       	Blocks: 8          IO Block: 4096   通常ファイル
Device: fd00h/64768d	Inode: 68539814    Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-01-25 00:27:06.220595863 +0900
Modify: 2022-01-25 00:27:04.607603930 +0900
Change: 2022-01-25 00:27:04.615603890 +0900
 Birth: 2022-01-25 00:27:04.607603930 +0900

touchコマンド

  • 空のファイルを作成したり、ファイルのタイムスタンプを変更する
  • 指定したファイルが存在しない場合、空ファイルが作成される
  • statコマンドでファイルの詳細情報は確認できる
オプション 説明
-t 時刻 アクセス時刻と修正時刻を指定した時刻に変更
-a アクセス時刻を変更
-m 修正時刻を変更

圧縮系コマンド

  • gzip, bzip2, xy の順に圧縮率が高い

gzipコマンド

  • gzip形式でファイルを圧縮する
  • 拡張子は .gz
  • 解凍は gzip -d もしくは gunzip

圧縮する

$ gzip test.txt

圧縮ファイルを解凍する

-dオプションで解凍
$ gzip -d test.txt.gz
gunzipコマンドでも解凍できる
$ gunzip test.txt.gz

圧縮前のファイルも残す

-cオプションで圧縮前のファイルも残す
$ gzip -c test.txt > test.txt.gz

ディレクトリの中身を圧縮する

-rオプションでディレクトリを圧縮
$ gzip -r testdir/

圧縮されたファイルの内容を表示: zcatコマンド

$ zcat test.tx
test

bzip2コマンド

  • gzpiと比較した時に圧縮率が高い、圧縮処理に時間がかかる
  • 拡張子は .bz2
  • オプションはgzipと似ている
  • 解凍は bzip2 -dまたは bunzip2
  • 元のファイルを残して圧縮するには、 -cオプション とリダイレクトの >を使う
  • bzip2 -c configure > configure.bz2

xzコマンド

  • bzipと比較してさらに圧縮率が高く、時間がかかる
  • オプションはgzip, bzip2と異なるので注意が必要
  • Linuxカーネルソースの配布形式としても採用されている
  • unxz コマンドでも展開できる
オプション 説明
-d, --decompress 圧縮ファイルの展開
-k, --keep 圧縮、展開処理後に元のファイルを削除しない
-l, --list 圧縮ファイルの情報を表示

アーカイブコマンド

tarコマンド

  • 複数のファイルをまとめて一つのファイルにアーカイブ、または展開する
  • オプションと圧縮形式の紐付けをしっかり覚える(zとgzipは音が似ている, jとbzipはjとiの形が似ている)
  • 似たようなコマンドは、だいたい任意オプションの -v
  • オプションの-は省略できる
  • 監修的にはc, f, tのような動作オプションは先頭につける
  • tarコマンドの最後のハイフンは、標準入力からファイル名を受け取ることを意味する
オプション 説明
-c 新しいアーカイブを作成
-x アーカイブからファイルを展開
-t アーカイブ内容の一覧を表示
-f アーカイブファイル名を指定
-v 処理の詳細情報を表示
-z gzipを通して圧縮/展開
-j bzip2を通して圧縮/展開
-J xzを通して圧縮/展開

cpioコマンド

  • cpio形式のアーカイブを扱う
  • アーカイブの一部が破損しても部分的な被害で済む可能性がある
  • 標準入力からのみ入力を受け付ける
  • oとcreateのcが似ているのでそれで覚える
  • iとoでcreateとextractと覚えそうだが、その逆
オプション 説明
-o 新しいアーカイブを作成
-i 新しいアーカイブを展開

ハードリンク

  • ファイルの実体を直接参照するリンク
  • inode番号は全て同じ
  • ファイルシステムが異なると作成できない

シンボリックリンク

  • シンボリックリンクを作成するにはln -s リンク元 リンク先
  • ls -lコマンドでファイル種別でlと表記される
  • 異なったファイルシステムでも作成可能

tailコマンド

  • ファイルの末尾部分を指定して表示するコマンド
オプション 説明
-n 行数 / - 行数 指定した行数をファイルの末尾から表示
-c バイト数 指定したバイト数のみ表示
-f, --follow ファイルの末尾に追加された行を表示し続ける

wcコマンド

  • ファイル内の文字数や行数を表示するコマンド
オプション 説明
-l 行数 行数を表示
-w 単語数を表示
-c 文字(バイト数を表示)

sortコマンド

  • 行単位でファイルの内容をソートするコマンド
オプション 説明
-k フィールド名 ソート対象とするフィールドを指定
-t 区切り文字 指定した文字を区切り文字として認識
-r 降順でソート
-n 数字を文字でなく数字としてソート
-f 大文字、小文字を区別しない
$ cat file.txt
bbb, 1000, ddd
ccc, 300, aaa
ddd, 335, xxx
aaa, 400, ppp

$ sort -f -k 2 -t , -r file.txt
aaa, 400, ppp
ddd, 335, xxx
ccc, 300, aaa
bbb, 1000, ddd

nlコマンド

  • 指定したファイルを行番号をつけて出力
オプション 説明
-b a 空行を含めた全ての行に行番号をつける cat -nと同じ
-b t 空行を除いた行に行番号をつける cat -bと同じ

splitコマンド

  • 指定した行数でファイルに分割するコマンド
  • デフォルトは1000行
  • 出力名を指定しない場合、「x」 + 「ファイル名」 + 「aaやab」がつく

fmtコマンド

  • 1行あたりの最大文字数を指定してテキストを整形
ログインするとコメントできます