Tag: Bash

bash/引数

bash によるオプション解析 - Qiita

テンプレート、get_blocklist()関数、get_mac_addresslist()関数、ヘルプを表示する usage_exit()関数は用意してあるものとする

if [ $# -eq 0 ]; then
  get_blocklist
  exit
fi

while getopts "bmh" OPT
do
    case $OPT in
        b)  get_blocklist
            hoge="hogehoge"
            ;;
        m)  get_mac_addresslist
            ;;
        h)  usage_exit
            ;;
        \?) usage_exit
            ;;
    esac
done

shift $((OPTIND - 1))

最後の shift $((OPTIND - 1)) だが、これはコマンドライン中のオプションの部分を削除する動作

注意点

getopts コマンドでオプションを使ったシェルプログラミングは楽しい ♪

source などで呼ぶと変数領域がそのまま引き継がれる様子。 個人的には bash だと全部グローバル変数なので、変数は汚染されやすいよな、と思う

Read more...

bash/比較演算子

bash の比較演算子の意味 - enokitech

bash であれば [[ ! -d /var/tmp ]] のような 2 重カッコを使うことで、否定ができる

演算子英語記号
-ltLess than<
-gtGreater than>
-leLess than or Equal to<=
-geGreater than or Equal to>=
-eqEqual to=
-neNot equal to!=

文字列比較

=左項と右項が同一文字列の場合真
!=でない場合真
str文字列が null でない場合真
-n文字列が null でない場合真(not zero)
-zの場合真(zero)
if [ "$1" = "$2" ]; then

ファイルに関するオプション

オプション効果
-dディレクトリなら真
-f普通のファイルなら真
-r読み出し可能なら真
-sサイズが 0 より大きければ真
-w書き込み可能なら真
-x実行可能なら真
if [ -f "file1" ]; then

条件式を論理演算するオプション

オプション効果
-a左項条件式と右項条件式のどちらも真の場合真(and)
-oどちらかが真の場合真(or)
!条件式の否定(not)
()()の中の条件式を優先する

[ での -a-o は解釈が分かりにくいので、bash では [[ ... && ... ]][[ ... || ... ]] を使う方が安全。

Read more...

bash周りのメモ

bash 5.1.4 で 2021/09/09 確認

文字列操作

VAR=onetwothree # 以下はこの VAR を操作するとして

表現結果意味
${VAR/two/four}onefourthree値の中の文字列 two を four に置換
$VARonetwothree(元の変数自体は変わらない)
${#VAR}11文字数
${VAR/two}onethree置換文字列を指定しないと一致文字列が削除される
${VAR#one*t}wothree値の前方からマッチする最短の文字列を取り除く
${VAR##one*t}hree値の前方からマッチする最長の文字列を取り除く
${VAR%t*ee}onetwo値の後方からマッチする最短の文字列を取り除く
${VAR%%t*ee}one値の後方からマッチする最長の文字列を取り除く
${VAR: 0: 3}one一番左の文字から 3 文字を取り出す
${VAR: 2: 3}etw2 番目の文字から 3 文字分を取り出す
${VAR: 2}etwothree第 2 パラメータを省略すると`末尾まで’の意となる
${VAR: -4: 3}hre末尾から数えて 4 番目の文字から、3 文字分を取り出す
${VAR: -4}hree第 2 パラメータを省略すると`末尾まで’の意
${VAR: -1}e最後の 1 文字を取り出すにはこう

シェルの組み込みコマンド

コマンド効果
:何もしないで終了コード 0 を返す。while : ;do …;done のように無限ループさせるのによく使う。
. filenamefilename で指定されたファイルを読み込んで中に書かれたコマンドを現在の環境で実行する。include のイメージ。
break [n]ループのを抜ける。n を指定すると n レベル外のループを抜ける。
continue [n]ループの次の繰り返し分から実行を継続する。n を指定すると n レベル外のループで実行を継続します。
read [name …]標準入力から 1 行読み込み、最初の単語を最初の name に代入し、2 番目の単語を 2 番目の name に代入します。name が指定されなかったら REPLY に代入される。
exec command [arguments]現在のシェルはこの command に置き換えられます。新しいプロセスは生成されません。exec 以降のスクリプトが実行されることはありません。
exit [n]ステータス n でシェルを終了させる。
return [n]ステータス n で関数を終了する。
export name[=word] …name を環境変数に設定します。word が指定された場合は環境変数 name に word が設定されます。
よく使う構文結果効果
$((1+1))2足し算
$((2-2))0引き算
$((2*2))4掛け算
$((2/2))1割り算
$((7%2))1余り
++num あるいは num++インクリメント
–num あるいは num–デクリメント
文字列分割して配列に入れる

old_ifs=$IFS
IFS=','
declare str='aaa,bbb,ccc'
declare -a record=($str)
IFS=$old_ifs
文字列の検索

declare str1='abcd'
declare str2='cd'
old_ifs=$IFS
IFS="$str2"
declare temp=($str1)
declare -i str1_len=${#str1}
declare -i temp_len=${#temp}
IFS=$old_ifs
if [ "$str1_len" -eq "$temp_len" ]; then
  declare -i result=-1 #見つからなかったら-1
else
  declare -i result=$temp_len # 見つかった場合はその位置
fi
read

データファイルを処理するためのちょっとしたスクリプトを書くのに ruby を使っていたけど,bash でもいろいろできるのね. # (1) パイプを使う

Read more...

bash_ リダイレクト

exec でリダイレクト先を指定する

ファイルを分ける

exec 2>> stderr.log 1>> stdout.log

ファイルを一緒にする

exec >> stdouterr.log 2>&1

リダイレクト 入力と出力 - UNIX & Linux コマンド・シェルスクリプト リファレンス

“{}” のグループをリダイレクト

{
echo "1. hogehoge"
echo "2. fugafuga"
echo "3. foofoo"
echo "4. barbar"
} >>file

ヒアドキュメントをリダイレクト

cat <<__END_OF_MESSAGE__ >>file
1. hogehoge
2. fugafuga
3. foofoo
4. barbar
__END_OF_MESSAGE__

for 文のブロックをリダイレクト

for i in `seq 1 10`; do
  echo "number $i"
done >>file

if [ -f "$str" ]; then
  ls "fugafuga"
  else
  echo "FUGAFUGA"
fi >>file

case "$str" in
"hoge" ) ls "hogehoge" ;;
"fuga" ) cat "fugafuga" ;;
* ) echo "NG..." ;;
esac >>file

関数全体をリダイレクト

func()
{
  [ $# -ne 3 ] && return 1

  echo "NO MESSAGE..."
  echo "NO ERROR MESSAGE..." 1>&2

  grep -v "$1" "$2" >"$3"
} >/dev/null 2>&1

複数コマンドの出力をまとめてパイプに流す

複数コマンドの出力をまとめてパイプに流し込みたいときは、 “{}” によるコマンドのグルーピング機能を利用する。

Read more...

bashコーディング規約

コーディング規約

参考 参考

  • ヘッダを記載する
  • ヘッダの直後に基本的な環境変数を定義する
    • umask
    • LANG
    • PATH
  • インデントは半角スペース 2 つ
  • | は前後にスペース 1 つ
  • 、»は前にスペース 1 つ、後ろは空けない

  • <、«も前にスペース 1 つ、後ろは空けない
  • if と then は同一行に、セミコロンの直後はスペース 1 つ
  • for、while の do と done は行頭を揃える
  • 関数定義の function は省略してもよい(しなくてもよい)
  • if 文を省略しない
  • 連続した同一ファイルへのリダイレクトはグルーピング
  • 変数の命名規則
    • 定数は大文字
    • 定数以外は小文字
    • 複数の単語を連結する場合は「_」で結合する
    • readonly 宣言を付けて、読み取り専用にすることを推奨
    • ファイル/ディレクトリの絶対パスの場合は、HOGE_FILE または FUGA_DIR
    • ファイル名またはディレクトリ名のみの場合は、HOGE_FILENAME または FUGA_DIRNAME
  • 今後増える見込みのない少ない繰り返しはループしない
  • 正常終了時はリターンコード 0
  • 異常終了時はリターンコード 0 以外
  • ログレベル
    • INFO: 参考情報
    • NOTICE: 注意レベル
    • WARNING: 警告レベル、処理を中断しないスキップ程度の問題
    • ERROR: エラー、処理を中断するレベルの問題

シェルスクリプトのコーディングルール 2014

参考

## 4. フィルタとパイプを意識する
UNIX ツールのほとんどはこの思想のもとに作成されている。データ入力には stdin を使用し、データ出力には
stdout を使用するべきである。こうすることで、パイプによるフィルタリング処理がしやすくなる。パイプでフィル
タしたデータを受け渡し出来るようになれば、他のツールとの連携が行える。

$ cat pipe.sh
#!/bin/bash
if [ -p /dev/stdin ]; then
    echo "Pipe"
    cat -
fi
$ echo "abc" | ./pipe.sh
Pipe
abc

こうすることでパイプを捕捉し、フィルタコマンドとしての役割を担うことができるようになります。
5. 変数を管理する

多くのプログラミング言語では、変数にはスコープが存在しますが、シェルスクリプトの場合、関数内で宣言した変数
もグローバル変数となります(明示的に local コマンドで局所変数化する必要がある)。またシェルスクリプトでは
、環境変数も扱うことが可能です。そこで以下のルールで管理すると良いでしょう(自分なりのルールがあればよい)

- 環境変数: 全て大文字(export VARIABLE)
- グローバル変数: 大文字から始まる(Variable)
- ローカル変数: 全て小文字(local variable)

また、定数は readonly を使用し、大文字で宣言します。

- readonly HISTORY_FILE="/path/to/hist_file"
- readonly HISTORY_FILE_NAME="hist_file"
Read more...

bashでのデバック時に参考にさせていただくページ

シェルスクリプトのデバッグ - UNIX & Linux コマンド・シェルスクリプト リファレンス

-x を使ったデバックの詳細がある。部分的に-x を適用したり。

たぶん使わないであろう Bash のデバッグ方法 - harry’s memorandum

-x -v でデバッグ

このページにはだいぶマニアックなデバッグ方法がある。

Read more...

bashで引数をIFS==で文字列を分解する

  • cat file | while read ... より while read ...; do ...; done < <(...) の方が、ループ本体を現在のシェルで実行できるので変数を保持しやすい。
  • IFS='=' read -r key value <<<"$pair" の形で key=value を分解できる。
  • 動的な変数代入が必要でも eval は避け、local "$key=$value" のように扱う方が安全。
create_tags() {
  local instance_id=$1
  shift

  local tags=""
  local pair key value

  for pair in "$@"; do
    IFS='=' read -r key value <<<"$pair"
    if [[ -z "$key" || -z "$value" ]]; then
      echo "$0 ${FUNCNAME[0]}: 第2引数以降は key=value の形式で指定してください" >&2
      echo " 受け取った値: ${key}=${value}" >&2
      exit 4
    fi
    local "$key=$value"
    tags="$tags Key=$key,Value=$value"
  done

  tags="$tags Key=Creator,Value=${USER}@${HOSTNAME}"
  echo "$tags"
  # aws ec2 create-tags --tags $tags --resources "$instance_id"
}
create_tags i-02d848a7 Name=inoue-test1 Nodes=default ROLES=ekitan_essence hogehoge=4
Read more...

bashのシグナルをトラップする

シグナルをトラップする方法

参考この人のシェルスクリプト関連の話題は深い… シェルスクリプトでシグナルをトラップして処理したい場合に参考になった。

tracebackの方はサブシェルで起動するようにすれば良いかも

EXIT トラップ内で pkill -P $$ を使う例は、親シェル配下の子プロセスをまとめて止めたい時だけ使う。意図しないプロセスまで終了させる可能性があるので注意。

#!/bin/bash
set -u # Check unset variables only
#set -ue # Check unset variables. Exit on error
LANG=C

# Trap signals
trap_HUP() {
  echo "Trap HUP signal."
  exit 1
}
trap_INT() {
  echo "Trap INT signal."
  exit 1
}
trap_TERM() {
  echo "Trap TERM signal."
  exit 1
}
trap_QUIT() {
  echo "Trap QUIT signal."
  exit 1
}
on_exit(){
  echo "Kill child processes on exit by 'pkill -P $$'"
  pkill -P $$
  echo "Exit."
}
on_error() {
  errcode=$?
  echo "error line $1: command exited with status $errcode."
}

trap 'trap_HUP' HUP
trap 'trap_INT' INT
trap 'trap_QUIT' QUIT
trap 'trap_TERM' TERM

# bash pseudo-signals
trap 'on_exit' EXIT
trap 'on_error $LINENO' ERR
Read more...

bash制御構文

分岐

if [ 条件 ] ; then
fi

if [ 条件 ] ; then
else
fi

if [ 条件 ] ; then
elif [ 条件 ] ; then
fi

declare -i i=0
while [ "$i" -lt 5 ]; do
  # 処理
  ((++i))
done

for 文

declare -i i
for ((i=0; i < 5; ++i)); do
done

for each 文

for field in "${fields[@]}";do
done

if 文をシンプルに

参考

Read more...

bash変数周りのメモ

シェル変数

||0|| プログラム名 ||1 ~ 9|| 第 1 ~9引数 ||@|| 全ての引数。"$@" としたときは “$1” “$2” “$3”… に等しい。 ||*|| 全ての引数。"$*" としたときは “$1 $2 $3…” に等しい。 ||#|| 引数の数 ||?|| 直前に実行したプログラムの終了コード。 ||RANDOM|| ランダムな数字。0~32767 が帰ってくる模様。 ||$|| プロセス ID

Geek なぺーじ:10 の UNIX 小技

変数と「"」の組み合わせは慎重に

特別な理由が無い限り、変数は「"」で囲う方が良いでしょう。
もし、変数名の直後に文字を使いたいのであれば、変数名を「{}」で囲いましょう。
これを忘れてしまうと、別の変数名として扱われてしまいます。
意図しない別の変数名になってしまった場合、大抵はnull値になるでしょう。

$ ls tmp/
a b
$ VAR="tmp/*"
$ echo $VAR
tmp/a tmp/b
$ echo "$VAR"
tmp/*
$ echo $VARa

$ echo "$VARa"

$ echo "${VAR}a"
tmp/*a
$ echo ${VAR}a
tmp/a
$

コマンドのグループ化

Geek なぺーじ:10 の UNIX 小技

Read more...

bashでcgi

bash で cgi かける

ただセキュリティ的に良くないので、外部からアクセスできない場所でのみ使えると思う。

#!/bin/sh
cat - <<%%EOF
Content-type: text/html

<html>
<body>cgi-test(/cgi-bin)test.hoge.com</body>
</html>
%%EOF
Read more...