Closed13

シェルスクリプトまとめ

ぱんだぱんだ

シェル文法

単純コマンド

  • 一般のUNIXコマンド。lsとか

複合コマンド

  • 構文(if, case, for, while)
  • サブシェル(リストを()で囲んだもの)
  • グループコマンド(リストを{}で囲んだもの)
  • シェル関数

if

コマンド

  • 単純コマンドと複合コマンドを合わせてコマンドと呼ぶ。

パイプライン

  • コマンドの標準出力を別のコマンドの標準入力に接続する。

リスト

  • パイプラインを改行などで区切って並べたもの
  • 改行とセミコロンは終端を意味するもので基本的にはそれまでのパイプラインを実行するという理解

&&

  • A && B Aのexitコードが0のときにBを実行

||

  • A || B Aのexitコードが0以外のときにBを実行
ぱんだぱんだ

if

if [ "$1" -eq 3 ]; then
  echo 'iの値は3です'
elif [ "$1" -eq 5 ]; then
  echo 'iの値は5です'
else
  echo 'iの値は3でも5でもありません'
fi
  • [もコマンド。testコマンドのエイリアス。
  • if[の間にはスペースが必要。じゃないとif[というコマンドで認識されてしまう
if [ "$1" -eq 3 ]
then
    :
fi
  • 何もしないときは:が必要。:は何もしないコマンド。

case

case $1 in
    3)
        echo 'iの値は3です'
    ;;
    5)
        echo 'iの値は5です'
    ;;
    *)
        echo 'iの値は3でも5でもありません'
    ;;
esac
  • パターンにはパス名展開の特殊文字を使って複数のパターンをマッチさせることもできる。*とか
  • caseのあとにはコマンド置換を使うこともできる

for

for elm in a b c; do
    echo $elm
done

for elm in a b c
do
    echo $elm
done
  • inとその後の値は省略ができる。省略した場合は$@を指定した場合と等しい。つまり引数の値を全てループに渡す
for elm; do
    echo $elm
done

for elm in "$@"; do
    echo $elm
done
  • inに続く値にはパス名展開を使用することもできる。
# カレントディレクトリの全てのファイル名を出力
for file in *; do
    echo $file
done
  • inに続く値にはコマンド置換(バッククォート)を使用することもできる
# filelistに記載のファイル名を全て出力
for file in `cat filelist`; do
    echo $file
done
  • continueも使える
for num in 1 2 3 4 5 6 7 8 9; do
    if [ $(( num % 2 )) -eq 0 ]; then
        continue
    fi
    echo $num
done
  • 以下のように算術式を利用して一般的なプログラミング言語のようなfor分も書ける(ただし、これはbashのみに限定されてしまうためどのシェルでも動くようにするためにwhile文を使った方が良い。)
sum=0
for((i = 0; i < 100; i++)) {
    (( sum += i ))
}
echo $sum

while

sum=0
i=0
while [ $i -lt 100 ]; do
    sum=`expr $sum + $i`
    i=`expr $i + 1`
done
echo $sum
  • 算術式が動くのはbashのみ
  • 以下のように指定されたコマンド引数をwhileループで処理もできる。その場合、shiftコマンドで引数を除去していく
while [ $# -gt 0 ]; do
    echo $1
    shift
done
  • :を使用して無限ループもできる
# 無限ループ
while :; do
    echo y
done
  • 代わりにtrueでもいいけど、シェルによってはtrueが外部コマンドなので:の方が効率が良い

select

PS3='コマンド? '

select cmd in up down left right quiet
do
    case $cmd in
        up)
            echo '上に移動しました'
            ;;
        down)
            echo '下に移動しました'
            ;;
        left)
            echo '左に移動しました'
            ;;
        right)
            echo '右に移動しました'
            ;;
        quiet)
            echo '終了します'
            break;;
        *)
            echo "値が不正です $REPLY"
    esac
done
  • bashのみしか対応していないので一般的に使用するのはやめたほうがいいかもしれない

PS3という変数について

知らなかったのでChatGPTに聞いてみた。

PS3は"Prompt String 3"の略です。シェルにはPS1からPS4までのプロンプト文字列変数があります。

PS1: 通常のコマンドプロンプトで使用される文字列。デフォルトではユーザー名、ホスト名、カレントディレクトリなどが表示されます。
PS2: シェルが追加の入力を待っている際に表示されるプロンプト(例:コマンドが完全でない場合)。デフォルトでは>が表示されます。
PS3: selectコマンドを使用した際に表示されるプロンプト。ユーザーに選択肢を入力させる際に使用されます。
PS4: スクリプトをデバッグモードで実行した際に、各コマンドの前に表示されるプロンプト。デフォルトでは+が表示されます。
ぱんだぱんだ

サブシェル

  • 別のシェルを起動してリスト()で括ったリストを実行する
  • グループコマンドと似てる。別のシェルを起動するところが異なっている。

グループコマンド

  • {}で括る。
  • 複数のコマンドをまとめて出力する時などに便利。
uname -a > logfile
date >> logfile
who >> logfile

{ uname -a; date; who; } > logfile
ぱんだぱんだ

関数

hello() {
    echo 'Hello'
}

function hello2() {
    echo 'Hello'
}
  • functionキーワードをつけることも可能だが、bashのみの対応なのでつけない方が良い
  • 関数内で定義した変数などはグローバル変数になるので関数外でも生き残る
  • localというキーワードも使えるけどこれも全てのシェルで使えるわけではないので非推奨
  • もしローカル変数を実現したければ関数をグループコマンドで括るのではなく()でサブシェルとして実行すればいける。
  • 関数は実行の前に定義する必要がある
ぱんだぱんだ

算術展開

(( ))で囲うことで一般的な算術ができる

(( i = 1 ))
while (( i < 10 )); do
    echo $i
    ( (i++))
done
  • ただ、これもbashしか対応していないのでやらないほうがいい
  • 代わりの書き方はexprで算術する

条件式の評価[[ ]]

  • if文の条件式にはtestコマンド([])を使用するが代わりに[[ ]]を使用することもできる
  • 使うと色々条件式が書きやすい
  • ただし、移植性が低いので使わない方がいい
ぱんだぱんだ

組み込みコマンド

:

  • nullコマンド
  • 何もしない
  • : ${1?'引数を指定してください'}のようなパラメータ展開だけを実行したいときとかに使える
  • : > fileのようにするとゼロサイズファイルが作れる
  • ゼロサイズファイルは既存のファイルを削除せず中身をゼロにすることができる

.

  • 現在実行中のシェルに別のシェルスクリプトを読み込む
  • .の代わりにsourceも使えるけどこれも移植性がないのでやめたほうがいい

break

  • forやwhileループを抜ける
  • 引数に数値を指定することができる
while :; do
    while :; do
        break 2
    done
done

echo '二重ループを抜けたよ'

continue

  • forやwhileをcontinueすることができる
  • breake同様引数に数値を指定することができる
counter=0
while [ "$counter" -lt 10 ]; do
    while :; do
        counter=$(expr "$counter" + 1)
        continue 2
    done
done

echo '終わったよ'

cd

  • ディレクトリ移動
  • 一旦ディレクトリ移動して戻りたいみたいな時はサブシェルを使用すると良い感じになる

eval

  • 引数を再度評価する
day1=Sunday day2=Monday day3=Tuesday

today=day1

eval echo \"\$$today\"

exec

  • 現在のプロセスで新たにプロセスを起動することなくコマンドを実行する
  • 以下のようなラッパースクリプトなどで余分なプロセスを起動することなく実行できる
LANG=C; export LANG
exec myprog
  • コマンドではなく標準入力を指定すると出力先を変更することができる

getopts

  • -付きの引数を解釈するのに使う
  • while文と通常一緒に使うことがほとんど
while getopts cvo: option; do
    case $option in
    c) echo 'オプションに-cが指定されました' ;;
    v) echo 'オプションに-vが指定されました' ;;
    o) echo 'オプションに-oが指定されました。引数は'"$OPTARG"'です' ;;
    \?) echo 'オプションが不正です。' ;;
    esac
done

echo "$OPTIND" "$@"

shift "$(expr $OPTIND - 1)"

if [ $# -gt 1 ]; then
    echo '残りの引数' "$@"
fi
  • cvo:-c-v-o 引数を読み込む
  • 読み込んだオプション一文字はoption変数に格納されるのでcase文で処理する
  • 引数ありのオプションの場合、OPTARGに入ってる
  • 指定外のオプションがあると?に入る
  • OPTINDには次に処理すべき位置パラメーターの番号が入る

read

  • ターミナルから入力を受け付ける
  • selectコマンドの代用で使える
while
    echo '1)up 
2)down
3)left
4)right
コマンド? ' 1>&2
    read -r cmd
do
    case $cmd in
    up) echo '上だよ' ;;
    down) echo '下だよ' ;;
    left) echo '左だよ' ;;
    right) echo '右だよ' ;;
    *) echo '不正な値だよ' ;;
    esac
    echo
done

readonly

  • 読み込み専用にする、unsetや代入ができなくなる
  • readonly TEST=testのように代入と同時にもできるけど二段階で分けた方が互換性は保たれる
  • 位置パラメーターや特殊パラメーターには使えない
  • サブシェルで使った場合の有効範囲はサブシェル内のみ

set

  • オプションフラグをセットする
# パス名展開でカレントのファイル全て表示
echo *
# -fオプションをセットしてパス名展開を禁止
set -f
# パス名展開されず*で表示
echo *
# -fオプションのセットを取り消し
set +f
# 元に戻る
echo *
  • 位置パラメーターをセットする
# 位置パラメーターをセット
set one two three

echo "$1" "$2" "$3"

# -始まりは`--`のあとに値を指定
opt=-a

set -- "$opt"

echo "$1"
  • シェル変数を一覧表示する
  • 上記3つの機能がある
  • オプションフラグのセットはシェルスクリプト先頭行のシェバンに続くシェル指定のオプションで宣言することもできる

shift

  • 位置パラメーターをずらす
  • while文と一緒に使うことが多い
while [ $# -gt 0 ]; do
    echo "$1"
    shift
done
  • 位置パラメーターをリセットしたいならshift $#でリセットできる

trap

  • シグナルを受け取った時に指定のコマンドを実行させる

シグナル一覧

SIGHUP (1)

ハングアップ。通常、制御ターミナルが切断されたときや親プロセスが終了したときに送信されます。
多くのデーモンプロセスでは、設定ファイルをリロードするトリガとして使用されます。
SIGINT (2)

キーボードからの割り込み(通常はCtrl+C)。
プロセスを中断して終了させる。
SIGQUIT (3)

Ctrl+\によって生成。プロセスを終了させ、コアダンプを生成する。
SIGILL (4)

不正な命令。プロセスが不正な命令を実行したときに送信される。
SIGTRAP (5)

トレースまたはブレークポイントトラップ。
SIGABRT (6)

アボート。プロセスがabort()関数を呼び出したときに生成。
SIGBUS (7)

バスエラー。
SIGFPE (8)

浮動小数点例外。
SIGKILL (9)

強制終了。プロセスを即座に終了させる。キャッチや無視、ブロックは不可能。
SIGUSR1 (10)とSIGUSR2 (12)

ユーザー定義シグナル。プログラム固有の用途で使用できる。
SIGSEGV (11)

セグメンテーション違反。
SIGPIPE (13)

パイプが壊れた(読み込み先のプロセスが終了しているなど)。
SIGALRM (14)

アラームクロック。alarm()関数によって設定されたタイマが切れたとき。
SIGTERM (15)

終了。プロセスに対する終了のリクエスト。SIGKILLよりもソフトで、プロセスがシグナルをキャッチしてクリーンアップを行うことができる。
SIGCHLD (17)

子プロセスが停止または終了したとき。
SIGCONT (18)

停止しているプロセスを継続。
SIGSTOP (19)

プロセスを停止させる。キャッチや無視は不可能。
SIGTSTP (20)

キーボードからの停止(通常はCtrl+Z)。
SIGTTIN (21)とSIGTTOU (22)

バックグラウンドプロセスが入力または出力を試みたとき。

type

  • 組み込みコマンドなのか外部コマンドなのかシェル関数なのかを調べる

外部コマンドはパスを表示

% type cp
cp is /bin/cp

組み込みコマンド

% type echo
echo is a shell builtin

シェル関数

% type func
func not found

% func() {:}

% type func 
func is a shell function

wait

  • 引数に指定したプロセス番号の処理を待つ
% (sleep 10; exit 25)&
% wait $!
% echo $?
  • 直前のプロセス番号を取得するには$!特殊パラメーターが使える
ぱんだぱんだ

外部版もある組み込みコマンド

echo

  • 連続した空白がなくなっちゃうのでシングルクォートで囲うと良い

false true

kill

  • 指定のシグナルをプロセスにとばす
  • 基本はシグナルを飛ばすとプロセスを停止させる

printf

  • フォーマットで出力できる
  • %dとか%sが使える
printf '%d + %d = %d\n' 1 1 2
printf 'Hello %s\n' Yamanaka

test

  • testのエイリアスが[
  • ifやwhileの条件として使われる

testコマンドで使用される演算子

  • -d ファイルがディレクトリなら真
  • -e ファイルが存在するなら真
  • -f ファイルが通常ファイルなら真
  • -z 文字列が空文字なら真
  • -n 文字列が空文字でなければ真
  • =, ==, !=, <, > 文字列に対してはこれらの演算子が使える
  • -eq, -ne, -lt, -le, -gt, -ge 数値に対してはこれらを使う

builtin command let local

ぱんだぱんだ

パラメーター

シェル変数

# 特殊記号などが展開されないように''で括ったほうがいい
msg='Hello'

echo "$msg"

# これでも参照はいい
echo ${msg}

# 変数に代償したコマンド名はそのまま参照して使える
cmd='echo'
"$cmd" "$msg"

# 特殊記号含む
a='*** Hello World!! ***'
# 変数の再代入時は""いらない
b=$a

echo "$b"

# これは展開した文字列の特殊記号がシェルに解釈されてしまう
echo $b

位置パラメーター

  • 引数の参照は$1, $2...のようにして位置パラメーターで参照できる
  • 位置パラメーターの参照は基本的にその値に含まれる記号などがパス展開などされないようにダブルクォーテーションで囲った方がいい
  • $0は特殊パラメーターの扱いでファイル名が入る
  • $@は特殊パラメーターで引数が全部参照できる
  • $#は特殊パラメーターで引数の数を参照できる
  • $*は特殊パラメーターで引数を全て連結して参照する。$@と似てるが$@の方が使う機会は多い
  • $?は特殊パラメーターで直前の終了ステータスを取得する
  • $!は特殊パラメーターで直前にバックグラウンドで起動したプロセスのプロセス番号を取得できる
  • $$は特殊パラメーターで自身のプロセス番号を取得する。これは一時ファイルの作成などによく使われる
  • $-は特殊パラメーターで現在のシェルに設定されているオプションフラグを参照する
  • $_は特殊パラメーターで直前に実行したコマンドの引数を参照する

環境変数

  • 単純コマンドの前に環境変数の上書きを行うとそのコマンド実行のみ環境変数の値を上書きできる
LANG=C date
ぱんだぱんだ

パラメーター展開

```${var:-default} ${var-default}

  • シェル変数varを読み込むが値が設定されていなかった場合defaultを使う
  • ${var:-default}の方は空文字も値が設定されてないとみなす
  • シェル変数だけでなく位置パラメーターなどのパラメーターに対して使える
var=
echo "${var:-default}" # default 空文字含む
echo "${var-default}" # 

```${var:=default} ${var=default}

  • 値が設定されてなかったらデフォルト値を代入する
  • : ${var:=default}とすることで事前に事項することができる

```${var:?error_message} ${var?error_message}

  • 値がセットされていなかったらエラーメッセージ
  • : ${1?引数を指定してください}みたいに事前に実行できる

```${var:+default} ${var+default}

  • 値が設定されてる場合にのみ右側の値を展開する

```${#パラメーター}

  • パラメーターの文字数をカウントする
var='test'
echo ${#var}

printf %s "$var" | wc -c
  • 全てのシェルで使えるわけではないのでwcコマンドで集計したほうがいい

```${パラメーター#パターン} ${パラメーター##パターン}

  • パラメーターからパターンに一致した部分を取り除く
  • #は最短一致、##は最長一致
dir='/usr/local/bin'

echo "${dir##*/}" # bin

basename "$dir" # bin

```${パラメーター%パターン} ${パラメーター%%パターン}

  • 右側からパターンに一致した部分を取り除く
  • %%は最長一致、%は最短一致
dir='/usr/local/bin'

echo "${dir%%/*}" # (全部消えちゃう)
echo "${dir%/*}" # /usr/local
dirname "$dir" # /usr/local

${パラメーター:オフセット(:長さ)}

  • パラメーターを切り出す
var='abcdef'
echo "${var:2:2}" #cd
echo "${var:2}" # cdef
  • 全てのシェルで使えるわけではないので非推奨

${パラメーター/パターン/置換文字列} ${パラメーター//パターン/置換文字列}

  • 置換する。
  • 全てのシェルで使えるわけではないので非推奨

${!シェル変数名@} ${!シェル変数名*}

  • 指定した文字列から始まる変数を一覧表示
  • 全てのシェルで使えるわけではないので非推奨

${!パラメーター}

message='hello'
var=message

echo "${!var}"
eval echo  "\$$var"
  • 間接参照は全てのシェルで利用できないのでevalを使用したほうが移植性は高いらしい
ぱんだぱんだ

クォートとコマンド置換

シングルクォート

  • パラメーターやパス展開などされない。そのまま文字列として扱う

ダブルクォート

  • パラメーターなどは展開される

バッククォート

  • コマンド置換。実行したコマンド結果を扱う。

$()

  • コマンド置換。移植性を考えるとバッククォートのがいい。
ぱんだぱんだ

各種展開

展開の順序

  • ブレース展開
  • チルダ展開
  • パラメーター展開/コマンド置換/算術式展開
  • 単語分割
  • パス名展開

パス名展開

  • * 0文字以上の任意の文字に一致するファイルを一覧表示。
  • ? ?の数分の任意の文字に一致するファイルを一覧表示
  • [a-z] []の中で指定した文字などに一致するファイルを一覧表示
  • set -fコマンドでパス展開を抑止できる

ブレース展開

  • 複数の文字列の組み合わせから文字列を生成する
touch test{1,2}.txt # test1.txt test2.txt
  • 連番の展開もできる
% echo {1..10}
1 2 3 4 5 6 7 8 9 10
  • a-zの展開も
% echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
  • set +Bでブレース展開を抑制できる

算術式展開

  • (())は算術式の評価だったが$(())とすることで算術結果をそのまま使用することができる

チルダ展開

  • ~でホームディレクトリを参照。移植性を考えて$HOMEを使用した方が良い
ぱんだぱんだ

リダイレクト

ファイル記述子

  • 0 標準入力
  • 1 標準出力
  • 2 標準エラー出力

標準エラー

  • 2> /dev/nullでエラーを捨てる
  • make > make.log 2>&1のようにして標準エラーを標準出力にリダイレクトすることもできる
  • スペースは入れてはいけない

ヒアドキュメント

# シングルクォートで囲うと完全に文字列として扱われる
base64 <<'EOF'
test test
test test
EOF

test='test'

# なので以下は変数展開されないよ
base64 <<'EOF'
$test $test
$test $test
EOF

# これは変数展開されるよ
base64 <<EOF
$test $test
$test $test
EOF

# こうすると行頭のタブを無視してくれるよ
base64 <<-EOF
  $test $test
  $test $test
EOF

ヒアストリング

  • cat <<< 'Hello World'のように文字列を標準入力から送ることができる
ぱんだぱんだ

配列

  • 配列はbashなら以下のように使用したりもできる
arr=[]
arr[0]=1

echo "${arr[0]}"

# 初期化と一括表示
arr=(one two three)
echo "${arr[@]}"
  • これらはbashのみの対応なので移植性を考慮するならevalを使用して擬似配列を使える
arr_0='one'
arr_1='two'
arr_2='three'

index=1

eval echo \"\$arr_$index\"
このスクラップは2023/10/07にクローズされました