1. HOME
  2. テックブログ
  3. みんな大好きなbashコマンドを極めてみる

みんな大好きなbashコマンドを極めてみる

2021/04/07 テクノロジー

目次

  • ▼概要
  • ▼一般コマンド系
  • ▼スクリプト系テクニック
  • 概要

    ちょこっとした処理をするには非常ぉ~~~に便利なbashコマンドやbashスクリプト。
    しかし、ちょっとクセがあったりして、そこまで頻繁に使うわけでないので、覚えるのはちょっと難あり。
    まあここでは、極めるってほどの話しではないですが、LinuxやUnix/BSD系で、自分もよく、ちょくちょく、たま〜に使う、便利そうなbashコマンドとその機能・オプション等を、私の独断と偏見で厳選して、備忘録的に記録しておきます。

    ※基本、Linux系メインで記載しておりますので、一部、Unix、BSD系、MAC等や、バージョンによってもオプション等が異なる場合がありますので、ご了承ください。

    一般コマンド系

    ファイル内検索(grep)

    主にgrepのお話し。よく使うものだけ抜粋。
    圧縮ファイルは、zgrep , bzgrepコマンドを使う。
    変数に特定の文字が含まれているかチェックする場合も活用。[ “$(echo $tmp|grep ‘aaa’)” ] など。

    基本形式

     grep [オプション] 検索文字列  ファイル名
    

    grep コマンド(オプション)

    オプション 機能など
    -i 大文字小文字を区別しない
    -w 単語で検索
    -E 拡張正規表現
    正規表現にエスケープ文字「¥」が不要になったりするので便利
    -v これに一致したものを除外
    -B数字 検索箇所のx行前も表示(Before)
    -A数字 検索箇所のx行後も表示(After)
    -r 指定したフォルダ以降を再帰的に全ファイルgrepしてくれる
    -l ファイル名のみ表示
    (複数ファイル存在時のみ有効)
    -h ファイル名を表示しない
    (複数ファイル存在時のみ有効)

    grep コマンド(正規表現)

    正規表現 機能など
    . 任意の1文字
    * 直前の文字を0回以上繰り返し
    ¥+ 直前の文字を1回以上繰り返し
    ¥{n,m¥}
    ¥{n¥}
    ¥{n,¥}
    直前の文字をm回以上n回以下の繰り返し
    直前の文字をちょうどn回の繰り返し
    直前の文字をn回以上の繰り返し
    ^ 行頭
    $ 行末
    [a-z]
    [A-Z]
    [0-9]
    範囲内の任意の1文字
    [^指定文字] 指定文字を除外して検索
    ¥(検索1¥|検索2¥) 「検索1」か「検索2」をor検索
    さらに複数あれば、「\|」パイプで繋げる
    ¥正規表現文字列 エスケープ文字。上記の正規表現を通常文字として扱う、
    ¥. →「.」ドット文字として扱う、など

    ファイル名検索

    ファイル名の検索だけです。 昔はlocateコマンドが非常に便利だったんですが、今は標準では亡き…。

    ファイル名検索

    パス配下のファイル名を検索。
    ワイルドカードを使う場合は、シングルコーテ ーション(’)で括る必要あり。

    find パス -name 'xxx*xxx.log'

    雑なやり方。
    ls -R で、フォルダ配下を再帰的に表示してくれる。
    権限とかファイル日時とかも探したいときに役立つ。

    ls -FlasR パス | grep "xxx*xxx.log"

    Webアクセス系(curl)

    Webアクセスするためのコマンド。 wgetはまぁいいや(笑)

    基本形式

     curl [オプション] URL [オプション]
    

    curlコマンド(オプション)

    オプション 機能など
    -i ヘッダ情報を最初に表示
    ボディ部も表示する
    -I ヘッダ情報のみを表示
    ボディ部は表示しない
    -s サイレント表示
    ダウンロード状況などの進捗を表示しない
    -sS 進捗はいらないけどエラーは表示される
    -k SSL/TLS証明書の正当性チェックをパスする
    自己証明書などはこれを使用
    -XPOST https://URL/ -d “POSTデータ” POSTデータの送信
    データは「aaa=1&bbb=2&・・・」の表記
    curl -c cookie.txt setクッキーの保存
    curl -b cookie.txt 保存されているクッキーを送信
    set cookieがあれば、ここに保存もしてくれる
    -o filename.log 結果をファイルに保存
    -O 結果をURL末尾の名前でファイルに保存
    -L リダイレクト対応。指定したlocationに自動でアクセスしてくれる
    デフォルトはリダイレクト非対応で、302とか返ってきても無視して結果のみ出力
    -H “User-Agent: Googlebot/2.1” ヘッダ情報を設定
    (ここの例ではユーザーエージェント)
    -F “file=aaa.log” ファイルのアップロード
    -u user:password BASIC認証

    置換系

    変数やパイプ後の置換など。

    sed コマンド

    検索文字列を置換文字列に置換。
    正規表現を使えば複雑な置換も可能。
    ‘s|文字列1|文字列2|g’ とか区切り文字は変更可能。

     echo "xxxx" | sed -e 's/検索文字列/置換文字列/g'

    -iオプションを付けると、ファイル直接置換する。
    -iオプションの後ろは、バックアップファイルを作成するときの末尾に付ける文字列を指定するが、バックアップ不要のときは、下記のように指定する。

    (Linux)
    sed -i  -e 's/検索文字列/置換文字列/g' filename
    (FreeBSD)
    sed -i '' -e 's/検索文字列/置換文字列/g' filename
    

    bash変数内の文字列置換

    コマンド(結果例) 機能など
    ${変数/検索文字列/置換文字列} 最初にマッチした部分のみ置換
    ${変数//検索文字列/置換文字列} マッチする部分、全て置換
    ${変数#先頭からの削除文字列}
    TMP=/var/www/html/tst/www/root
    echo ${TMP#/*/*/*/}
    tst/www/root
    先頭からの最短マッチ部分を削除
    ここら辺以降はあんまり使わないかな。。
    ${変数##先頭からの削除文字列}
    echo ${TMP##*/}
    root
    先頭からの最長マッチ部分を削除
    ${変数%パターン}
    echo ${TMP%/*}
    /var/www/html/tst/www
    末尾からの最短マッチ部分を削除
    ${変数%%パターン}
    echo ${TMP%%html/*}
    /var/www/
    末尾からの最長マッチ部分を削除

    (おまけ)bash変数のオフセット位置からの取得

    最初の数値がオフセット位置、その後が文字列長さを指定。

    $ tmp="abcdef"
    $ echo ${tmp:0:2}
    ab
    

    文字列長さを省略すると、最後まで出力。

    $ echo ${tmp:2}
    cdef
    

    文字列長さがマイナスは、最大文字列長さから引いた長さを返してくれる。

    $ echo ${tmp:0:-2}
    abcd
    

    改行削除

    改行削除されて、全てくっつく。

    $ TMP="aaaa
    bbbb
    
    cccc
    "
    $ echo "$TMP" | tr -d '¥n'
    aaaabbbbcccc
    

    sed版。sedはそのままだと改行コードを置換できないので、下記のようにする必要がある。 めんどいのであんまり使わないほうが良いかな…。

    (Linux)
    $ echo "$TMP" | sed ':loop; N; $!b loop; s/¥n//g'
    (FreeBSD)
    $ echo "$TMP" | sed -e ':loop' -e 'N; $!b loop' -e 's/¥n//g'
    $ aaaabbbbcccc
    

    ダブルクォーテーションで括らないと、改行の区切りがスペースになる。

    echo $TMP
    aaaa bbbb cccc
    

    空行削除

    sedとか使うと複雑になるので、これが一番シンプルイズベスト。

    $ echo "$TMP" | grep -v "^$"
    aaaa
    bbbb
    cccc
    

    計算

    簡易な足し算、引き算、割り算、掛け算など。

    expr コマンド(整数のみ)

    整数のみ計算。小数点は切り捨て。
    演算の優先順位(掛ける・割る優先)も考慮される。
    「%」は割り算の余り。

    $ tmp=3
    $ expr 7 - 12 \* 4 / $tmp
    -9
    

    bc コマンド(小数点以下OK)

    小数点以下の計算。 scaleで小数点以下、何桁まで表示するか設定。

    $ echo "scale=4; 10.3/3.3-100" | bc
    -96.8788
    

    演算精度を保つために計算結果は小数点以下6桁を保持し、表示は小数点以下2桁の場合。

    $ tmp=$(echo "scale=6; 10.3/3.3-100" | bc)
    $ echo $tmp
    -96.878788
    $ printf "%.2f" $tmp
    -96.88
    

    bash組み込みインクリメント・デクリメント (加算・減算)

    $(()) 内はインクリメント演算子が使え、$を書いても書かなくてもOK。
    先頭に記述の場合、評価前にインクリメントした値を返す。

    $ ii=3; echo "$((++ii))  $ii"
    4  4
    $ ii=3; echo $((--ii))
    2  2
    

    末尾に記述の場合、評価後にインクリメントした値を返す。

    $ ii=3; echo "$((ii++))  $ii"
    3  4
    $ ii=3; echo "$((ii--))  $ii"
    3  2
    

    先頭に記述の場合と同等。

    $ ii=3; echo "$((ii+=1))  $ii"
    4  4
    $ ii=3; echo "$((ii-=1))  $ii"
    2  2
    

    ii=$((++ii))のような書き方をしなくても単体で使用可能。
    また $? の返り値は、評価結果が0の場合は0、それ以外は1が返る特性がある。

    $ ii=3; let ++ii; echo $ii
    4

    日時計算

    日時の表示や計算など

    dateコマンド

    現在の日時(秒以下まで)を表示。
    $(date ~) などで変数やファイル名などに入れたり、[ $(date +%Y%m%d) -lt 20210407 ] などで日時の比較にも便利。

    $ date +%Y%m%d_%H%M%S.%N
    20210326_094453.491610548
    

    曜日

    $ date +%a
    金
    

    UTC (1970-01-01 00:00:00) からの秒数(Unix時間)

    $ date +%s
    1616719469
    

    Unix時間から戻す。

    (Linux)
    $ date --date "@1616719469" 
    (BSD系)
    $ date -r 1305730800
    2021年  3月 26日 金曜日 09:44:29 JST
    

    2021/03/03から、1年前と1ヵ月後。
    (Linux)単位は他にday,hour,minute,secが使用可能。
    (BSD系)単位は他にw(週),d,H,M,Sが使用可能。

    (Linux)
    $ date --date '20210303 1 year ago 1 month ' +%Y-%m-%d
    (BSD系)
    $ date  -v-1y -v+1m -j 202103030000 +%Y-%m-%d
    2020-04-03
    

    日時比較

    本日が 2022/01/01 より過去なら、~を実行。
    ※-lt などの判定分は後述参照。

    [ $(date +%Y%m%d) -lt 20220101 ] && ~
    

    スクリプト系テクニック

    主にスクリプトファイルを書くときに便利なものなど。

    定番系

    PATH設定

    とりあえずこんな感じで、スクリプトの先頭に設定しておけば、めったにフルパスでコマンド記述しなくても良いかな~って感じ。

    export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    

    言語

    基本、日本語が好きな人向け。
    環境によって適宜。

    export LANG=ja_JP.UTF-8
    

    小技・便利系

    スクリプトのカレントディレクトリ設定

    現在実行しているスクリプトのディレクトリ名をここに設定。
    これを最初に設定しておけば、他の関連ファイル・ログ等を作成する場合に、このディレクトリを起点に色々相対パスで指定できるので便利。

    SCRIPT_DIR=$(cd $(dirname $0);pwd)
    LOG=$SCRIPT_DIR/tst.log
    

    スクリプトのファイル名

    現在実行しているスクリプトのファイル名をここに設定。
    usageなどを作成する場合に便利。

    SCRIPT_NAME=$(basename $0)
    

    フォルダ作成

    とりあえず「-p」オプションでフォルダ作成しておけば、フォルダがあってもエラーが出ないので、いちいちフォルダ存在チェックがいらない。(おうちゃくですみません。。。)

    mkdir -p /xxx/xxx/xxx/
    

    ファイルのクリア

    いちいち「ファイル存在チェック→削除→クリエイト」としなくても、一発で済む。ファイルがなければ作成もするし。
    オプション「-n」があると、完全クリア。
    オプション「-n」がないと、最初に改行コードが入るが、通常は気にしないレベルかと。

    echo -n > bbb.log
    echo  > bbb.log
    

    ファイルのインクルード

    先頭に「.」を付けることで、共通的に使用するようなファイルを、その場所に持って来る形でincludeできる。

    . ./define.conf
    

    ファイルの中身を1行ずつ読み込む

    注意すべきとして、このケースでは、while内の変数はループ終了後に変数の値を引き継がない。

    $ { tmp=ABC
    cat ファイル名 | while read LINE;do
      tmp="$LINE"
    done
    echo $tmp ; }
    	ABC
    

    下記の場合は、whileループ終了後でも変数がそのまま使える。
    基本、こちらが推奨。

    $ { tmp=ABC
    while read LINE; do
      tmp="$LINE"
    done < ファイル名
    echo $tmp ; }
    (結果はファイルの末尾の行を表示)
    

    CSVファイル(コンマ(,)区切り)の場合の例

    while IFS=, read column1 column2 ; do
      echo "$column1 $column2"
    done < CSVファイル名
    

    この場合、改行だけでなく、スペースも区切り文字として分割されてしまうので注意。

    for LINE in $(cat ファイル名) ;do
      echo "$LINE"
    done
    

    変数系

    特殊変数

    特殊変数 機能など
    $0 実行した際のファイル名(パス含む)
    $1~n 実行時の引数。1番目、2番目、、、n番目
    $# 引数の数
    $@ 引数すべて
    $? 直前のコマンドの実行結果の返り値
    通常、正常時:0以上、異常時:-1以下
    値はコマンドによりけり

    引数のオプション解析

    下記はgetopts による解析例。
    デメリットとして、引数のあとにオプションを使用できない、ロングオプション不可がある。

    getopts直後の「:」コロンについて。
    先頭の「:」は、エラーを自前で処理。なければ自動でエラー表示。
    文字列のあとの「d:」は、オプションの後の引数を$OPTARGで取得可能。

    ※もっと多機能を求めるならGNU版getoptをお勧めしますが、BSD系との互換性を考えると、とりあえずgetopts レベルで良いかな。そこまでガッツリ作るつもりないし(笑)

    while getopts ":ad:h" OPT
    do
      case $OPT in
        a) AllFlg=1;;
        d) optDir=$OPTARG ;;
        h) usage;exit ;;
        :) echo  "Error: オプションに引数がないよ。"; usage; exit -2;;
        ¥?) echo "Error: そんなオプションはないよ。"; usage; exit -1;;
      esac
    done
    
    # getopts分の引数値移動
    shift $(($OPTIND - 1))
    

    自前で解析する場合。この例では、変数opt_1,opt_2,・・・に引数1、引数2、・・・が入る。

    ii=0
    for OPT in "$@"
    do
    	case "$OPT" in
    		-a | --all) AllFlg=1;;
    		-d) [ "${2:0:1}" = "-" ] && { echo "Error: -d 引数ないよ。";exit -1;} ; optDir="$2";shift ;;
    		-*) echo "Error: そんなオプションはないよ。"; usage; exit -1;;
    		*) eval "opt_$ii='$OPT'";let ++ii ;;
    	esac
    	shift
    done
    

    if判定

    形式

    形式 機能など
    if [ 条件 ] ; then
    # 正の処理
    [else
    # 偽の処理 ]
    fi
    通常のif文
    [ 条件 ] && 正の処理 [ || 偽の処理 ] 簡略系
    [ 条件 ] && {
    正の処理
    正の処理
    ・・・ ; } [ || 偽の処理 ]
    複数行の例

    条件 - ファイル・ディレクトリの判定

    条件 機能など
    [ -f ファイル名] ファイルが存在しているなら真
    [ -s ファイル名] サイズが 0 より大きければ真
    [ -d フォルダ名 ] ディレクトリが存在しているなら真
    [ -e ファイルorフォルダ名] ファイルかディレクトリが存在しているなら真
    [ -r ファイルorフォルダ名] 読み取り権限があれば真
    [ -w ファイルorフォルダ名] 書き込み権限があれば真
    [ -x ファイルorフォルダ名] 実行権限があれば真

    条件 - 数値の比較

    コマンド 機能など
    [ $tmp -eq 1 ] = イコール
    [ $tmp -ne 1 ] != 否定
    [ $tmp -lt 1 ] < 左側が小さい場合、真
    [ $tmp -le 1 ] <=
    [ $tmp -gt 1 ] > 左側が大きい場合、真
    [ $tmp -ge 1 ] >=

    条件 - 文字列の比較

    コマンド(結果例) 機能など
    [ "$tmp" = "test" ] イコール
    [ "$tmp" != "test" ] 否定
    [ -z $tmp ]
    [ "$tmp" = "" ]
    文字列長が 0 の場合、真
    [ -n $tmp ] 文字列長が 0 より大きい場合、真

    その他bash制御系

    whileはまあいいや。

    for文

    ループを抜ける場合は break;
    ループの先頭に戻って継続する場合は continue;

    コマンド(結果例) 機能など
    for ((ii=0; ii < 10; i++)); do
    echo $ii
    done
    定番の指定回数分ループ
    for 変数 in 値のリスト; do 値のリストは、スペース区切り

    最後に

    なんか色々書いていたら、思っていたよりボリューミーになってしまって。もっと簡単に書きたかったのですが…。

    まだまだbashには奥深い技やコマンドが多々ありますので、また機会があれば第2弾があるかもしれません。

    ▼第2弾

    この記事を書いた人

    インフラMr.Y
    プロダクト開発本部 インフラチーム
    インフラMr.Y

おすすめの記事