プロジェクト

全般

プロフィール

ShellScript » 履歴 » リビジョン 2

リビジョン 1 (kanata, 2025/04/13 15:54) → リビジョン 2/3 (kanata, 2025/04/13 15:54)

# ShellScript 

 目指せシェル芸人 

 {{rawhtml(<canvas id="map"></canvas><script src="/javascripts/pagemap.min.js"></script><script>pagemap(document.querySelector("#map"));</script>)}} 

 {{toc}} 

 主にbashについて書きます 

 # Basic 

 ## if文 

 ``` 
 if [ 条件1 ] 
 then 
   処理1 
 elif [ 条件2 ] 
 then 
   処理2 
 else 
   処理3 
 fi 
 ``` 

 ### 数値比較 

 |数値評価演算子    | 意味                                     | 
 |----------------|----------------------------------------| 
 |数値1 -eq 数値2 | 数値1と数値2が等しい場合に真             | 
 |数値1 -ne 数値2 | 数値1と数値2が等しくない場合に真         | 
 |数値1 -gt 数値2 | 数値1が数値2より大きい場合に真           | 
 |数値1 -lt 数値2 | 数値1が数値2より小さい場合に真           | 
 |数値1 -ge 数値2 | 数値1が数値2より大きいか等しい場合に真 | 
 |数値1 -le 数値2 | 数値1が数値2より小さいか等しい場合に真 | 

 こういう書き方 

 ``` 
 if [ "1" -eq "0" ] 
 ``` 

 <br><br><br><br><br> 

 ### 文字列比較 

 |文字列評価演算子     | 意味                              | 
 |-------------------|---------------------------------| 
 |文字列 	             | 文字列の長さが0より大きければ真 | 
 |-n 文字列            | 文字列の長さが0より大きければ真 | 
 |! 文字列             | 文字列の長さが0であれば真         | 
 |-z 文字列            | 文字列の長さが0であれば真         | 
 |文字列1 = 文字列2    | 2つの文字列が等しければ真         | 
 |文字列1 != 文字列2 | 2つの文字列が等しくなければ真     | 

 こういう書き方 

 ``` 
 if [ "ABC" = "abc" ] 
 if [ -z "${FILEPATH}" ] 
 if test -z "${FILEPATH}" 
 ``` 

 ### ファイルチェック 

 |ファイルチェック演算子    | 意味                                   | 
 |------------------------|--------------------------------------| 
 |-d ファイル名             | ディレクトリなら真                     | 
 |-f ファイル名             | 通常ファイルなら真                     | 
 |-L ファイル名             | シンボリックリンクなら真               | 
 |-r ファイル名             | 読み取り可能ファイルなら真             | 
 |-w ファイル名             | 書き込み可能ファイルなら真             | 
 |-x ファイル名             | 実行可能ファイルなら真                 | 
 |-s ファイル名             | サイズが0より大きければ真              | 
 |ファイル1 -nt ファイル2 | ファイル1がファイル2より新しければ真 | 
 |ファイル1 -ot ファイル2 | ファイル1がファイル2より古ければ真     | 

 こういう書き方 

 ``` 
 if [ -f "${FILEPATH}" ] 
 if test -f "${FILEPATH}" 
 if [ "${FILE1}" -nt "${FILE2}" ] 
 ``` 

 ### 論理結合 

 |論理結合演算子 	 | 意味                                     | 
 |---------------|----------------------------------------| 
 |! 条件           | 条件が偽であれば真                       | 
 |条件1 -a 条件2 | 条件1が真、かつ、条件2が真であれば真     | 
 |条件1 -o 条件2 | 条件1が真、または、条件2が真であれば真 | 

 こういう書き方 

 ``` 
 # AND(論理積演算子) 
 if [ -f a.txt -a -f b.txt ] 
 if [ "$a" -eq "$b" ] && [ "$c" -eq "$d" ] 

 # OR(論理和演算子) 
 if [ -f a.txt -o -f b.txt ] 
 if [ "$a" -eq "$b" or "$c" -eq "$d" ] 
 if [ "$a" -eq "$b" ] || [ "$c" -eq "$d" ] 

 # NOT(否定論理演算子) 
 if [ ! "$a" -eq "$b" ] 
 ``` 

 ### 変数に任意の文字が含まれているかどうか 

 ``` 
 if echo $WORD | grep "任意の文字" >/dev/null 
 then 
   echo "文字含まれている" 
 else 
   echo "文字含まれていない" 
 fi 
 ``` 

 ### bashのワイルドカード 

 実はif文内でワイルドカードの判定ができる。 

 ```bash 
 STRING="abc" 
 if [[ $TEST == *"b"* ]] 
	 echo "matched" 
 else 
	 echo "unmatched" 
 fi 
 ``` 

 ワイルドカード部分 * は、ダブルコーテーションで囲んだりすると動かなくなるので注意。 

 ### bashの正規表現 

 そもそもgrepを使わないでも、変数に任意の文字が含まれているかどうか判定できる。 

 ```bash 
 STRING="abc" 
 if [[ "$STRING" =~ ^ab[cd]$ ]]; then 
	 echo "matched" 
 else 
	 echo "unmatched" 
 fi 
 ``` 

 正規表現部分 \^ab[cd]$ は、ダブルコーテーションで囲んだりすると動かなくなるので注意。 

 ## 展開 

 bash入門 
 https://rat.cis.k.hosei.ac.jp/article/linux/bash_intro.html 

 ### ブレース展開 

 カンマ区切りの文字列をブレースで囲んだものはブレース展開されます。 

 ``` 
 $ echo {a,b,c} 
 a b c 
 ``` 

 ブレースの前後に文字列があるとそれを反映した展開がなされます。 

 ``` 
 $ echo 0{a,b,c}1 
 0a1 0b1 0c1 
 ``` 

 ブレース展開はまとめてファイルやディレクトリを作成するときなどによく使われます。 

 ``` 
 $ find . -type d 
 . 

 $ mkdir -p a/b{0,1}/c{0,1} 
 $ find . -type d 
 . 
 ./a 
 ./a/b0 
 ./a/b0/c0 
 ./a/b0/c1 
 ./a/b1 
 ./a/b1/c0 
 ./a/b1/c1 
 ``` 

 ### チルダ展開 

 チルダはユーザーのホームディレクトリに展開されます。 

 ``` 
 $ whoami 
 user1 
 $ echo ~ 
 /home/user1 
 ``` 

 ### ファイル名展開 

 ファイル名展開はグロブ(glob)とも呼ばれます。 

 | パターン | 効果                                                                                                      | 
 |----------|---------------------------------------------------------------------------------------------------------| 
 | *          | 0文字以上の任意の文字列にマッチ。                                                                         | 
 | ?          | 1文字の任意の文字列にマッチ。                                                                             | 
 | [...]      | ブラケットで挟まれている文字のうち任意の1文字にマッチ。正規表現におけるブラケットの解釈とほぼ同じです。 | 


 ``` 
 $ ls 
 a    adc.txt    aec.txt    b 

 $ echo * 
 a adc.txt aec.txt b 

 $ echo ? 
 a b 

 $ echo a[abcd]* 
 adc.txt 

 # マッチするものが無かった場合パターンはそのままの形で残ります 
 $ echo xyz* 
 xyz* 
 ``` 

 ### 変数展開 

 変数を参照する場合に文字列を加工することができる。 

 | パターン                  | 効果                                                                                   | 
 |-------------------------|--------------------------------------------------------------------------------------| 
 | ${変数名#word}            | 変数から値を取り出し、その先頭部分がwordと一致した場合その部分を取り除きます。(先頭から前方最短一致した位置まで) | 
 | ${変数名##word}           | 変数から値を取り出し、その先頭部分がwordと一致した場合その部分を取り除きます。(先頭から前方最長一致した位置まで) | 
 | ${変数名%word}            | 変数から値を取り出し、その後方部分がwordと一致した場合その部分を取り除きます。(末尾から後方最短一致した位置まで) | 
 | ${変数名%%word}           | 変数から値を取り出し、その後方部分がwordと一致した場合その部分を取り除きます。(末尾から後方最長一致した位置まで) | 
 | ${変数名/pattern/word}    | 変数から値を取り出し、最初にpatternとマッチする部分だけをwordで置換します。            | 
 | ${変数名/#pattern/word} | 変数から値を取り出し、最初にpatternとマッチする部分だけをwordで置換します。(上と同じ)| 
 | ${変数名/%pattern/word} | 変数から値を取り出し、最後にpatternとマッチする部分だけをwordで置換します。            | 
 | ${変数名//pattern/word} | 変数から値を取り出し、patternとマッチする全ての部分をwordで置換します。                | 
 | ${変数名:=word}           | 変数が NULL の場合 word に置換され、かつ変数に代入される。                             | 
 | ${変数名:-word}           | 変数が NULL の場合 word を出力する。                                                   | 
 | ${変数名:+word}           | 変数が NULL 以外の場合 word に置換され、かつ変数に代入される。                         | 
 | ${変数名:?word}           | 変数が NULL の場合、標準エラー出力にwordを表示し、現在実行中のスクリプトを中止する。 | 
 | ${#変数名}                | 変数に設定されている文字数を返す。                                                     | 
 | ${変数名:offset}          | 変数に設定されているoffset文字+1から出力する。offsetを負の値にすると語尾から数える。 | 
 | ${変数名:offset:length} | 変数に設定されているoffset文字+1からlength分出力する。                                 | 
 | ${変数名,}                | 先頭だけ小文字に変換                                                                   | 
 | ${変数名,,}               | 全部小文字に変換                                                                       | 
 | ${変数名\^}                | 先頭だけ大文字に変換                                                                  | 
 | ${変数名\^\^}               | 全部大文字に変換                                                                     | 

 ### 算術式展開 

 expr や、bcコマンドを使用しなくても、$((計算式))で計算ができる。 

 ``` 
 $ echo $(( 1 + 2 + 3 )) 
 6 
 ``` 

 Qiita - Bash $((算術式)) のすべて 
 https://qiita.com/akinomyoga/items/2dd3f341cf15dd9c330b 


 ### コマンド置換 

 コマンド置換には以下の 2 つの形式があります。 

 ``` 
 $(command) 
 `command` 
 ``` 

 今までは一般的にバッククォート `command` が使われてきたが、$(command) も使えます。 
 $(command)は、入れ子ができるので、$(command)の方を使ったほうが良い。 

 ~~~ 
 $ TEST=`date`;echo $TEST 
 2016年 8月 28日 日曜日 18:04:33 JST 
 $ TEST=$(date);echo $TEST 
 2016年 8月 28日 日曜日 18:04:41 JST 
 ~~~ 

 次のコマンドはカレントディレクトリの中で「abc」を含むすべてのファイルの先頭10行を表示します。 

 ``` 
 $ head -10 $(grep -l abc *) 
 ``` 

 ### ヒアドキュメント 

 こんな感じ。ftpぐらいなら自動化できる。 

 ```bash 
 $ command << token 
 text 
 token 
 ``` 

 例 

 ```bash 
 #!/bin/bash 

 # Script to retrieve a file via FTP 

 FTP_SERVER=ftp.nl.debian.org 
 FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom 
 REMOTE_FILE=debian-cd_info.tar.gz 

 ftp -n << _EOF_ 
 open $FTP_SERVER 
 user anonymous me@linuxbox 
 cd $FTP_PATH 
 hash 
 get $REMOTE_FILE 
 bye 
 _EOF_ 
 ``` 

 ### ヒアストリング 

 変数を擬似的にファイルのように扱える。 

 ``` 
 $ cat < hoge 
 -bash: hoge: そのようなファイルやディレクトリはありません 
  cat <<< hoge 
 hoge 
 $ HOGE=hogefuga 
 $ cat < $HOGE 
 -bash: hogefuga: そのようなファイルやディレクトリはありません 
 $ cat <<< $HOGE 
 hogefuga 
 ``` 

 ## 特殊なシェル変数 

 | 変数 | 内容                                                   | 
 |------|------------------------------------------------------| 
 | $0     | 実行したスクリプトの文字列                             | 
 | $1, $2, ..., $9 | 第1引数, 第2引数, ..., 第9引数              | 
 | $#     | 与えられた引数の数                                     | 
 | $*     | 与えられたすべての引数. 引数全体が"で囲まれている      | 
 | $@     | 与えられたすべての引数. 引数一つ一つが"で囲まれている| 
 | $-     | シェルに与えられたフラグ                               | 
 | $?     | 最後に行ったコマンドの戻り値                           | 
 | $$     | 現在のシェルのプロセス番号                             | 
 | $!     | 最後にバックグラウンドで行ったコマンドのプロセス番号 | 
 | $_     | 実行シェルの文字列                                     | 
 | IFS    | 区切り文字のリスト                                     | 


 ## シェル変数と環境変数 

 |              | 特徴                                     | 確認 | 設定                                | 削除 |  
 |------------|----------------------------------------|------|-----------------------------------|------| 
 | シェル変数 | シェル変数は子プロセスに引き継がれない | set    | 変数名=値                           | unset # シェル変数、環境変数両方消去される 
 | 環境変数     | シェル変数は子プロセスに引き継がれる     | env    | export 変数名 or export 変数名=値 |unset # シェル変数、環境変数両方消去される 

 >ちなみに、環境変数を定義するには export コマンド以外にも declare コマンドに -x オプションを付けて実行する方法もある。 


 ## プロセス置換 

 以下の文法に従えば、コマンドの実行結果を一時ファイルに吐いておく必要がなくなる。 

 ~~~ 
 <(command) 
 ~~~ 

 例 

 ~~~ 
 $ sdiff <(date;sleep 1;date) <(date;date) 
 2016年    8月 28日 日曜日 18:00:15 JST                              2016年    8月 28日 日曜日 18:00:15 JST 
 2016年    8月 28日 日曜日 18:00:16 JST                            | 2016年    8月 28日 日曜日 18:00:15 JST 
 ~~~ 

 ## 連想配列 

 連想配列が使える。 

 >ver4系から使える機能です。Macのbashはver3系のため、brew等でアップデートしましょう。 

 ``` 
 $ declare -A hashTable 
 $ hashTable["hoge"]="fuga" 
 $ echo ${hashTable["hoge"]} 
 fuga 
 ``` 

 # bash開発機能 

 ## 文法チェック 

 ``` 
 $ bash -n test.sh 
 ``` 

 ## 実行内容をトレース 

 ``` 
 $ bash -x test.sh 
 ``` 

 ## ステップ実行 

 擬似シグナルDEBUGを使う。擬似シグナルDEBUGはシェルが文を実行するたびに発行される。 

 ``` 
 #!/bin/bash 

 trap 'read -p "next(LINE:$LINENO)>> $BASH_COMMAND"' DEBUG 
 ``` 

 ## 未定義の変数をエラーとして扱う 

 ``` 
 #!/bin/bash 
 set -u 

 # ${TEST}が未定義ならエラーになる 
 echo ${TEST} 
 ``` 

 ## 終了ステータスが0以外ならその時点で終了 

 ``` 
 #!/bin/bash 
 set -e 

 #ls -yでエラーになり、後続の処理は実行されない 
 ls -y 
 ls -l 
 ``` 



 # Sample 

 ## ファイルを一行ずつ読み込んで処理する 

 ``` 
 #!/bin/sh 
 # ファイルを1行ずつ読み込んで表示 

 TESTFILE=./hoge.txt 
 while read LINE 
 do 
     echo $LINE 
 done < $TESTFILE 
 ``` 

 処理単位も行にしたい時は、冒頭に以下を書く 

 ``` 
 IFS=' 
 ' 
 ``` 

 ## 区切り文字(デミリタ)で区切られた文字列の任意のフィールドを取得する 

 cut方式とawk方式がある 

 ``` 
 echo "abc:def:ghi:jkl"|cut -d: -f3 
 echo "abc:def:ghi:jkl"|awk -F':' '{print $3}' 
 ``` 

 ダブルクォーテーションで囲まれている場合 

 ``` 
 $ grep 設定項目名 設定ファイル | sed 's/[^"]*"\([^"]*\)"[^"]*/\1/g' 
 ``` 

 シングルクォーテーションで囲まれている場合 

 ``` 
 $ grep 設定項目名 設定ファイル | sed "s/[^']*'\([^']*\)'[^']*/\1/g" 
 ``` 

 イコールで定義している場合 

 ``` 
 $ grep 検索する設定項目 設定ファイルPATH | sed 's/ //g' | awk -F'=' '{ print $2}' 
 ``` 

 ## 標準入力とファイルの内容を結合 


 ~~~ 
 $ cat test 
 efg 
 $ echo -n abcd |cat - test 
 abcdefg 
 ~~~ 

 >大抵のコマンドには - (標準入力の内容をファイル相当として扱う)を実装している 


 ## サーバと連続でやりとりするシェルスクリプト 

 以下は、サーバからもらった計算式を計算して、送り返している例 

 ``` 
 #!/bin/sh 

 exec 5<>/dev/tcp/[host]/[port] 

 for I in {1..101} 
 do 
   cat 0<&5>test.txt & 
   sleep 1 
   pkill cat 
   WORD=`cat test.txt|tail -1|sed 's/=//g'` 
   ANSWER=`ecgo ${WORD}|bc` 
   echo ${ANSWER} > &5 
   echo Debug [${I}] ${WORD} '=' ${ANSWER} 
 done 

 exit 0 
 ``` 

 ## 文字列⇔16進数表現⇔2進数表現 

 文字列→16進数表現 

 ``` 
 echo "ABC"|xxd 
 ``` 

 文字列→2進数表現 

 ``` 
 echo "ABC"|xxd -b 
 ``` 

 16進数表現→文字列 

 ``` 
 echo 4142434445464748 | xxd -ps -r 
 ``` 

 16進数表現→2進数表現 

 ``` 
 echo 4142434445 | xxd -ps -r|xxd -b 
 ``` 

 2進数表現→16進数表現 

 ``` 
 echo "obase=16; ibase=2; 010000010100001001000011010001000100010100001010" | bc 
 ``` 

 2進数表現→文字列 

 ``` 
 echo "obase=16; ibase=2; 010000010100001001000011010001000100010100001010" | bc|xxd -ps -r 
 ``` 

 ---- 

 bcコマンドなくても、このぐらいはできる。 

 2進数表現→10進数表現 

 ``` 
 echo $((2#101010)) 
 echo "obase=10; ibase=2; 101010" | bc 
 ``` 

 8進数表現→10進数表現 

 ``` 
 echo $((04567)) 
 echo "obase=10; ibase=8; 4567" | bc 
 ``` 

 16進数表現→10進数表現 

 ``` 
 echo $((0xABCD)) 
 echo "obase=10; ibase=16; ABCD" | bc 
 ``` 

 ## 指定した相対PATHからの、絶対パス・ディレクトリ名・ファイル名の取得 

 ``` 
 $ cd /tmp/trash 
 $ readlink    -f test.txt  
 /tmp/trash/test.txt 
 $ dirname `readlink    -f test.txt` 
 /tmp/trash 
 $ basename `readlink    -f test.txt` 
 test.txt 
 ``` 


 ## IPアドレスの取得 

 いろんな方法があるが、NICのインタフェース名が不定だし、複数NICが刺さっていたりもするので、これという方法が無い。 

 ``` 
 $ ifconfig eth1 | grep -w 'inet' | cut -d: -f2 | awk '{ print $1}' 
 $ ip addr list venet0 | grep "inet " | cut -d' ' -f6 | cut -d/ -f1 
 $ ip a s | grep "inet " | cut -d' ' -f6 | cut -d/ -f1 
 $ hostname -i 
 $ hostname -I 
 ``` 

 実は[こういう技](https://ex1.m-yabe.com/archives/4638)がある 
 素晴らしい 

 ``` 
 $ curl ifconfig.io 
 ``` 



 ## IPアドレスのソート 

 ``` 
 $ cat ip_list.txt | sort -n -t'.' -k1,1 -k2,2 -k3,3 -k4,4 
 ``` 

 ## 特定のファイルのバックアップファイルを作成する 

 ``` 
 $ find PATH -type f -name 'ファイル名' | xargs -n 1 -I{} cp {} {}.bk 
 ``` 

 ## 特定のファイルをバックアップディレクトリにコピーする 

 ``` 
 $ find PATH -type f -name 'ファイル名' | xargs -n 1 -I{} cp {} バックアップ先PATH 
 ``` 

 ## 特定のファイルに対してのみgrepを行う 

 ``` 
 $ find PATH -type f -name 'ファイル名' | xargs -n 1 -I{} grep '検索キーワード' {} 
 ``` 

 ## 指定したファイルの合計サイズを取得する 

 ``` 
 $ du -bhc [ファイルPATH等条件] | tail -n 1 
 ``` 

 または 

 ``` 
 $ ls -la [ファイルPATH等条件] | awk '{ total += $5 }; END { print total }' 
 ``` 

 ## 対話型コマンドを自動実行する 

 対話中に判断や計算が入るとexpectでやるしかない。 

 ``` 
 #!/bin/expect -f 

 spawn ./121-calculation 

 set i 0 
 while {$i <= 100000} { 

 puts "\n#### $i ######" 

 expect "*=" 
 set quest $expect_out(buffer) 

 set answer [exec echo $quest | tail -1 | tr -d '=' | tr -d '\r' | bc] 
 send "$answer\n" 

 incr i 1 
 } 
 ``` 

 命令解説 

 * spawn コマンド実行 
 * puts 画面表示(実行したコマンドには影響しない) 
 * expect コマンド出力する文字にマッチングするまでここで止まる 
 * set 変数 定数 変数設定 
 * set 変数 $expect_out(buffer) コマンド実行結果を変数に(ただし、直前のではなく、全部入るっぽい) 
 * set 変数 [外部コマンド] 外部コマンドの実行結果を変数に(最新行を選択するtail -1 と 変な改行コード入ってエラーになるので tr -d '\r' を入れるのがコツ) 
 * send "変数名\n" コマンドに実行結果を送る(データ終端を\nにしているコマンドが多いので、明示的に\nを書く) 
 * incr 変数名 定数 加算 

 ## どういう形式で圧縮されているか判別して展開する 

 環境合わせて、適当に修正が必要 

 ``` 
 #!/bin/sh 

 LIST=`ls|grep -v flatting.sh` 

 for WORD in ${LIST} 
 do 
   FILE_TYPE=`file ${WORD}` 

   if echo ${FILE_TYPE}|grep "Zip archive data" >/dev/null 
   then 
     unzip ${WORD} 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "shell archive text" >/dev/null 
   then 
     sh ${WORD} 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "bzip2 compressed data" >/dev/null 
   then 
     bunzip2 -c ${WORD} > ${WORD}_out 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "POSIX tar archive" >/dev/null 
   then 
     tar xvf ${WORD} 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "gzip compressed data" >/dev/null 
   then 
     gunzip -c ${WORD} > ${WORD}_out 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "xz compressed data" >/dev/null 
   then 
     xz -d -c ${WORD} > ${WORD}_out 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "ASCII cpio archive" >/dev/null 
   then 
     cpio -idv < ${WORD} 
     rm ${WORD} 
   elif echo ${FILE_TYPE}|grep "ASCII text" >/dev/null 
   then 
     if cat ${WORD} |grep "This is dummy file" >/dev/null 
     then 
       rm ${WORD} 
     else 
       base64 -d ${WORD} > ${WORD}_out   
       if [ ${?} = 0 ] 
       then 
         rm ${WORD} 
       fi 
     fi 
   else 
     : 
   fi 

 done 

 exit 0 
 ``` 

 ## 一定時間ごとに任意のコマンドを実行する 

 もちろんwatchコマンドを使ってもいいが、以下の方法でも可能。 

 ``` 
 $ yes '[任意のコマンド]; sleep [任意のインターバル秒数];' | sh 
 ``` 

 ## 文字列への色つけ 

 ``` 
 txtund=$(tput sgr 0 1)      # Underline 
 txtbld=$(tput bold)         # Bold 
 txtred=$(tput setaf 1)      # Red 
 txtgrn=$(tput setaf 2)      # Green 
 txtylw=$(tput setaf 3)      # Yellow 
 txtblu=$(tput setaf 4)      # Blue 
 txtpur=$(tput setaf 5)      # Purple 
 txtcyn=$(tput setaf 6)      # Cyan 
 txtwht=$(tput setaf 7)      # White 
 txtrst=$(tput sgr0)         # Text reset 

 echo "${txtbld}This is bold text output from shell script${txtrst}" 
 ``` 

 ## プログレスバーの表示 

 ``` 
 show_progress() { 
     echo -ne '#####                       (33%)\r' 
     sleep 1 
     echo -ne '#############               (66%)\r' 
     sleep 1 
     echo -ne '#######################     (100%)\r' 
     echo -ne '\n'     
 } 
 ``` 

 ## スピナーの表示 

 ``` 
 show_spin () { 
   rotations=3 
   delay=0.1 
   for i in `seq 0 $rotations`; do 
     for char in '|' '/' '-' '\'; do 
       echo -n $char 
       sleep $delay 
       printf "\b" 
     done 
   done 
 } 
 ``` 

 ## あらゆるファイルの解凍 

 ``` 
 function extract { 
  if [ -z "$1" ]; then 
     # display usage if no parameters given 
     echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>" 
  else 
     if [ -f $1 ] ; then 
         # NAME=${1%.*} 
         # mkdir $NAME && cd $NAME 
         case $1 in 
           *.tar.bz2)     tar xvjf ../$1      ;; 
           *.tar.gz)      tar xvzf ../$1      ;; 
           *.tar.xz)      tar xvJf ../$1      ;; 
           *.lzma)        unlzma ../$1        ;; 
           *.bz2)         bunzip2 ../$1       ;; 
           *.rar)         unrar x -ad ../$1 ;; 
           *.gz)          gunzip ../$1        ;; 
           *.tar)         tar xvf ../$1       ;; 
           *.tbz2)        tar xvjf ../$1      ;; 
           *.tgz)         tar xvzf ../$1      ;; 
           *.zip)         unzip ../$1         ;; 
           *.Z)           uncompress ../$1    ;; 
           *.7z)          7z x ../$1          ;; 
           *.xz)          unxz ../$1          ;; 
           *.exe)         cabextract ../$1    ;; 
           *)             echo "extract: '$1' - unknown archive method" ;; 
         esac 
     else 
         echo "$1 - file does not exist" 
     fi 
 fi 
 } 
 ``` 

 > このコマンドも有用と思われ 

 俺的備忘録 〜なんかいろいろ〜 - ファイルの圧縮方式に合わせて自動的に解凍してくれる『dtrx』コマンド 
 https://orebibou.com/2016/07/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%9C%A7%E7%B8%AE%E6%96%B9%E5%BC%8F%E3%81%AB%E5%90%88%E3%82%8F%E3%81%9B%E3%81%A6%E8%87%AA%E5%8B%95%E7%9A%84%E3%81%AB%E8%A7%A3%E5%87%8D%E3%81%97%E3%81%A6/ 

 ## POSIX仕様の中でif文中でパターンマッチングする 

 ``` 
 if [ -n "$X" -a -z "${X%%pattern}" ] ; then echo "true"; else echo "false"; fi 
 ``` 
















 # KnowHow 

 シェルスクリプトを極める 
 http://www.slideshare.net/bsdhack/ss-43064758 

 ## バッファリングさせない 

 stdoutがttyじゃないとbufferingするコマンドは多い 
 stdbufで解決できる 


 ``` 
 $ command | stdbuf -oL command | command 
 ``` 

 >行単位でバッファを吐き出す 

 ``` 
 $ command | stdbuf -oO command | command 
 ``` 

 >バッファリングさせない 


 以下が神解説です 

 俺的備忘録 〜なんかいろいろ〜 - sedやawk、grepをバッファさせずに動作させる 
 https://orebibou.com/2017/06/sed%e3%82%84awk%e3%80%81grep%e3%82%92%e3%83%90%e3%83%83%e3%83%95%e3%82%a1%e3%81%95%e3%81%9b%e3%81%9a%e3%81%ab%e5%8b%95%e4%bd%9c%e3%81%95%e3%81%9b%e3%82%8b/ 


 ## ファイルディスクリプタ 

 * 標準入力は 0 
 * 標準出力は1 
 * 標準工ラー出力は2 

 シエルではリダイレクト記号の前に数字 

 ``` 
  &> ファイル    # 標準出力・標準エラー出力を共にリダイレクト 
  3> ファイル    # fd3をファイルにリダイレクト 
  4< ファイル    # fd4をファイルからリダイレクト 
  5<&-           # fd5をクローズ  
  6>&7           # fd7をfd6に複製(fd6をfd7にリダイレクト) 
 ``` 

 --- 
 実装 


 ``` 
 command > file 
 ``` 

 >結果、fdが標準出力として使用される 

 ``` 
 command > file 2>&1 
 ``` 

 >結果、fdが標準出力、および、標準エラー出力として使用される 

 ``` 
 commandA |commandB 
 ``` 

 >結果、commandA の標準出力が commandB の標準入力になる 


 ``` 
 $ ./echo "test" 3>&1 1>&2 2>&3 
 ``` 

 >標準エラー出力と標準出力を入れ替える 



 ## プロセスの出力同士をdiffで比較 

 ``` 
 $ diff <(commandA)<(commandB) 

 $ commandA|(commandB|diff /dev/fd/3 -) 3<&0 
 ``` 

 > 3<&0 で標準入力(fd0) を fd3 に複製 
 > commandA の出力が fd3 に出力される 
 > /dev/fd/3 からの入力が commandA の出力 

 ## パイプの途中のプロセスの終了コード 

 ``` 
 $ exec 3>&1  
 ret1=`{ { commandA 1; echo $? 1>&3; } | commandB 2; } 3>&1`  
 ret2=$? 
 ``` 

 > コマンド 1 の $? を 1>&3 で fd3 に出力 
 > fd3 の内容を 3>&1 で ret1 に格納 

 ## ループ処理でのファイルディスクリプタの活用 

 ``` 
 exec 3<&0 0<<EOF 
  `command`  
 EOF  
 while read line  
 do 
   :  
 done  
 exec 0<&3 3<&- 
 ``` 
 >exec 3<&0 で fd0 を fd3 に複製  
 >exec 0<<EOF でヒアドキュメントを fd0 として使用  
 >exec 0<&3 で複製した fd0 を復帰  
 >exec 3<&- で fd3 をクローズ  
 >パイプを使わないのでプロセスが生成されない 


 ## evalを用いた配列的な変数アクセス 

 ``` 
 # $1: 配列名  
 # $2: インデックス  
 # $3-: 値  

 setarray()  
 { 
   _index_="__`echo $1 $2 | sed -e 's/ //g'`" 
   shift 2 
   eval $_index_'='"$@"  
 } 

 getarray()  
 { 
   eval echo '${'__`echo $* | sed -e 's/ //g'`'}'  
 } 
 ``` 

 ## ファイルの上書き 

 元ファイルを書き替える 

 ``` 
 $ コマンド < ファイル > ファイル # 絶対ダメ! 
 $ (rm ファイル ; コマンド > ファイル ) < ファイル # ファイルの中身が消えない方法 
         ②                       ③            ① 
 ``` 

 >① でファイルが読み込みモードでオープンされる。 
 >② でオープンされたファイルが削除される。 
 >③ で新しい(別な)ファイルが書き込みモードでオープンされる。  

 ①のファイルと③のファイルは同じファイル名ですが、 inode が異なっているため、別のファイルとして扱われる。 

 1<>を使う方法もある 

 ``` 
 cat srcfile 1<> srcfile 
 ``` 

 ## シェルスクリプトでの排他処理 

 ``` 
 lockfile="/var/tmp/`basename ${0}`"  
 test -f ${lockfile} || touch ${lockfile} 
 ``` 

 >test(1) と touch(1) の間に他のプロセスの test(1) が実行されると 排他処理が失敗する  

 ``` 
 lockfile="/var/tmp/`basename ${0}`"  
 if ln -s $$ ${lockfile} 2> /dev/null  
 then 
   : # メイン処理を実行  
 else 
   echo "${0}: exist another instance" 1>&2  
   exit 255  
 fi  
 trap 'rm -f ${lockfile}; exit' 0 1 2 3 11 15 
 ``` 

 >シンボリックリンクの作成は atomic 
 >PID をリンクする事でロックしたプロセスが特定できる  

 ## 英単語の先頭文字を大文字に変換する 

 ``` 
 $ echo "foo" | awk '{ print toupper(substr($0, 1, 1)) substr($0, 2, length($0) - 1) }' 
 ``` 

 >awk 組み込みの substr() 、 toupper() を使用する  

 ``` 
 $ eval eval `echo "foo" | sed 's/(.)(.*)//bin/echo -n 1 | tr "[a-z]" "[A-Z]"; echo 2/g'` 
 ``` 

 >sed 単体では実現できないのでコマンド列を出力して eval 

 ``` 
 $ echo foo bar buz|sed 's/\b\([a-z]\)/\U\1/g' 
 ``` 

 >実はsed単体でできました 

 ## IF文のような分岐処理をワンライナーで行わせる 

 ``` 
 $ コマンド && TRUE || FALSE 
 ``` 

 「&&」以降はコマンドがTRUEの場合に、「||」以降はFALSEの場合に実行されるコマンドとなる。 

 例 

 ``` 
 $ cat test.log  
 abc [31/Oct/2015:20:36:51 +0900] ghi [20] 
 jkl [31/Oct/2015:20:36:55 +0900] pqr [0] 
 stu [31/Oct/2015:20:37:00 +0900] yz    [40] 
 000 [31/Oct/2015:20:37:05 +0900] ghi [5] 
 $ grep abc test.log >/dev/null && echo 1 || echo 2 
 1 
 $ grep azc test.log >/dev/null && echo 1 || echo 2 
 2 
 ``` 


 ## exの活用 

 ファイルの行操作に ex を活用する 

 - 直接行の追加や削除が可能(一時ファイル不要) 
 – 実は vi なので正規表現など強力な編集操作が可能 
 – echo やヒアドキュメントで編集コマンドを指定可能 

 ### exで行の追加 

 ``` 
 $ /bin/ex -s ファイル << EOF 
 行番号 a 
 コンテンツ 
 コンテンツ 
     : 
 . 
 w! 
 EOF 
 ``` 

 >行番号で指定した行の下にコンテンツを挿入する 
 > . で挿入モードを終了し w! で内容をファイルに出力する 

 ### exで行削除処理 

 ``` 
 $ /bin/ex -s ファイル << EOF 
 行番号 d 
 w! 
 EOF 
 ``` 

 >行番号で指定した行を削除する 
 > w! で内容をファイルに出力する 

 ### exで行番号を指定した行置換処理 

 ``` 
 $ /bin/ex -s ファイル << EOF 
 行番号 s/パターン/置換文字列/ 
 w! 
 EOF 
 ``` 

 >行番号で指定した行のパターンを置換文字列に置換する 
 > w! で内容をファイルに出力する 

 ### exでパターンを指定した行置換処理 

 ``` 
 $ /bin/ex -s ファイル << EOF 
 /パター /s/ 置換文字 / 
 w! 
 EOF 
 ``` 

 >最初に発見したパターンを置換文字列に置換する 
 >s の後ろの連続した // は直前の正規表現(パターン)を示す 
 > w! で内容をファイルに出力する 

 ### exでファイルから指定された行を削除する  

 ``` 
 $ cat basefile 
 first line 
 second line 
 third line 
     : 
     : 


 $ cat target 
 2 
 3 
 5 
 : 
 : 


 ``` 

 >元のファイル (basefile) には複数の行が含まれている 
 >削除する行は別なファイル (target) に格納されている 
 >target ファイルには行番号が 1 行ずつ格納されている 

 パターンを指定した行置換処理 

 ``` 
 $ sort target | awk '{ printf "%ddn", $1-(NR-1); } END { print "w!" }' | ex -s basefile 
 ``` 

 >削除済みの行を考慮する必要がある 
 >>→ 2 行目を削除すると今までの 3 行目が 2 行目になる 
 >ex で行を削除する 

 >awk で削除した行を考慮した行番号に対する行削除を出力  



 ## シェルスクリプトで標準入力を受け取る 

 3パターンある。 

 ### cat - で 標準入力を受け取り、利用できる。 

 ``` 
 #!/bin/sh 
 if [ -p /dev/stdin ] ; then 
     a=$(cat -) 
     echo "input string is ${a}" 
 else 
     echo "nothing stdin" 
 fi 
 ``` 

 ### readを使うと、標準入力を読むことができる 

 ``` 
 #!/bin/bash 
 while read i ; do 
     #数字を読み込んで1足して出力する。 
     echo $((i+1)) 
 done 
 ``` 

 他の方法に比べて高コストになりがち 

 ### /dev/stdinを使う 

 ``` 
 #!/bin/bash 
 awk '{print $1+1}' < /dev/stdin 
 ``` 

 ## 一時ファイルを作らずにコマンドの出力結果を利用する(Command Substitution) 

 <(コマンド)記法、ksh、bash、zsh限定。 

 コマンドに対する入出力を、ファイルのように指定することが出来る。 

 * コマンドの標準出力を入力ファイルとして <(コマンド) 
 * コマンドの標準入力を出力ファイルとして >(コマンド) 

 例えば、 

 ``` 
 $ diff <(ls) <(ls -a) 
 ``` 

 これと同義 

 ``` 
 $ ls > a 
 $ ls -a > b 
 $ diff a b 
 ``` 

 ## パイプラインに任意の値を入れ込む 

 ### 文字列入れ 

 ``` 
 $ seq 2 | (echo 'Header'; cat; echo 'Footer') 
 Header 
 1 
 2 
 Footer 
 ``` 

 ``` 
 $ seq 5 | (echo 'obase=2'; cat) | bc 
 1 
 10 
 11 
 100 
 101 
 ``` 

 ### ファイル入れ 

 "-"が標準入力のこと 

 ``` 
 $ seq 11 12 | cat data - 
 1 
 2 
 11 
 12 
 ``` 

 ## パイプで渡したコマンドの終了ステータスを得る 

 ``` 
 $ cat test.txt |cat|cat|cat|cat|cat 
 $ echo ${PIPESTATUS[@]} 
 0 0 0 0 0 0 
 ``` 

 ## bashでパイプで受け取った値を自動エスケープして出力する 

 俺的備忘録 〜なんかいろいろ〜 - bashでパイプで受け取った値を自動エスケープして出力する 
 https://orebibou.com/2017/07/bash%e3%81%a7%e3%83%91%e3%82%a4%e3%83%97%e3%81%a7%e5%8f%97%e3%81%91%e5%8f%96%e3%81%a3%e3%81%9f%e5%80%a4%e3%82%92%e8%87%aa%e5%8b%95%e3%82%a8%e3%82%b9%e3%82%b1%e3%83%bc%e3%83%97%e3%81%97%e3%81%a6/ 

 ## echo、printfでパイプから受けた値を出力する 

 俺的備忘録 〜なんかいろいろ〜 - echo、printfでパイプから受けた値を出力する 
 https://orebibou.com/2017/07/echo%e3%80%81printf%e3%81%a7%e3%83%91%e3%82%a4%e3%83%97%e3%81%8b%e3%82%89%e5%8f%97%e3%81%91%e3%81%9f%e5%80%a4%e3%82%92%e5%87%ba%e5%8a%9b%e3%81%99%e3%82%8b/ 

 ## サロゲートペアやUnicode結合文字を考慮して文字単位に分解する 

 NG 

 ``` 
 $ echo そのチェㇷ゚は𩸽🇯🇵 | grep -o .|tr '\n' '/' 
 そ/の/チ/ェ/ㇷ/゚/は/𩸽/🇯/🇵 
 ``` 

 OK 

 ``` 
 $ ruby -e 'puts "そのチェㇷ゚は𩸽🇯🇵".scan(/\X/).join("/")' 
 そ/の/チ/ェ/ㇷ゚/は/𩸽/🇯🇵 
 ``` 








 # awk KnowHow 

 AWK 一行野郎百裂拳 - 日本 GNU AWK ユーザー会 - No-ip.org 
 http://gauc.no-ip.org/awk-users-jp/material/100_one_liners_20131223.pdf 

 > 神 

 ## 特定の N 行を表示する 

 ``` 
 $ awk 'NR==N' 
 ``` 

 ## 空行を削除する 

 ``` 
 $ awk 'NF' 
 $ awk '$0' 
 $ awk '/./' 
 ``` 

 ## 文字数をカウントする(wc -c) 

 ``` 
 $ awk '{n+=length($0)} END{print n}' 
 ``` 

 ## 単語数をカウントする(wc -w) 

 ``` 
 $ awk '{n+=NF} END{print n}' 
 ``` 

 ## 行数をカウントする(wc -l) 

 ``` 
 $ awk 'END{print NR}' 
 ``` 

 ## 行末の空白やタブを削除する 

 ``` 
 $ awk '{sub(/[ \t]+$/, "")}1' 
 ``` 

 ## Unix の改行コードに変換する 

 ``` 
 $ awk 'sub(/\r$/,"")' 
 ``` 

 ## Windows の改行コードに変換する 

 ``` 
 $ awk 'sub(/$/,"\r")' 
 ``` 

 ## 逆順出力をする(tac) 

 ``` 
 $ awk '{a[i++]=$0} END{for(j=i-1; j>=0;) print a[j--]}' 
 ``` 

 ## 重複するレコードを削除する(uniq) 

 ``` 
 $ awk '!a[$0]++' 
 ``` 

 ## ソートしないで重複行を削除する 

 ``` 
 awk '!a[$0]++' FILE 
 ``` 

 ## 行番号を付ける(nl) 

 ``` 
 $ awk '$0 = NR OFS $0' 
 ``` 

 ## 標準出力にそのまま出力する(cat -) 

 ``` 
 $ awk '1' 
 ``` 

 ## 正規表現にマッチした行を表示する(grep) 

 ``` 
 $ awk '/hogehoge/' 
 ``` 

 ## 正規表現にマッチしない行を表示する(grep -v) 

 ``` 
 $ awk '! /hogehoge/' 
 ``` 

 ## コメント行を削除する 

 ``` 
 $ awk '! /^#/' 
 ``` 

 ## C言語のように複数行にまたがったコメント行を削除する 

 /* と */ に囲まれた行の場合 

 ``` 
 $ cat file | awk '/\/\*/, /\*\//{next}{print}' 
 ``` 

 ## 指定行から指定行までを表示する 

 ``` 
 $ awk 'NR==10,NR==20' 
 ``` 

 ## 偶数行を表示する 

 ``` 
 $ awk 'NR%2==0' 
 ``` 

 ## 奇数行を表示する 

 ``` 
 $ awk 'NR%2' 
 ``` 

 ## 特定のフィールド数を持つ行のみを抜き出す 

 ``` 
 $ cat file 
 りんご 100円 192個 
 ばなな 170円 210個 
 爽健美茶 150円 
 グラタン ソース マカロニ チーズ じゃがいも 
 $ cat file | awk 'NF>=2 && NF<=3' 
 りんご 100円 192個 
 ばなな 170円 210個 
 爽健美茶 150円 
 ``` 

 ## awkで\[\]\(カギカッコ\)内の値に応じて行を抽出する 

 ``` 
 $ awk -F '[][]' '$4 >= 〇〇' ログファイルPATH 
 ``` 

 例 

 ``` 
 $ cat log.txt 
 abc [31/Oct/2015:20:36:51 +0900] ghi [20] 
 jkl [31/Oct/2015:20:36:55 +0900] pqr [0] 
 stu [31/Oct/2015:20:37:00 +0900] yz    [40] 
 000 [31/Oct/2015:20:37:05 +0900] ghi [5] 
 $ awk -F '[][]' '$4 >= 20' log.txt 
 abc [31/Oct/2015:20:36:51 +0900] ghi [20] 
 stu [31/Oct/2015:20:37:00 +0900] yz    [40] 
 ``` 

 ## 異なるデミリタを指定して、要素を取り出す 

 ``` 
 $ cat log.txt 
 abc [31/Oct/2015:20:36:51 +0900] ghi [20] 
 jkl [31/Oct/2015:20:36:55 +0900] pqr [0] 
 stu [31/Oct/2015:20:37:00 +0900] yz    [40] 
 000 [31/Oct/2015:20:37:05 +0900] ghi [5] 
 $ awk -F '[][]' '{print $1}' log.txt 
 abc  
 jkl  
 stu  
 000  
 $ awk -F '[][]' '{print $2}' log.txt 
 31/Oct/2015:20:36:51 +0900 
 31/Oct/2015:20:36:55 +0900 
 31/Oct/2015:20:37:00 +0900 
 31/Oct/2015:20:37:05 +0900 
 $ awk -F '[][]' '{print $3}' log.txt 
  ghi  
  pqr  
  yz   
  ghi  
 $ awk -F '[][]' '{print $4}' log.txt 
 20 
 0 
 40 
 5 
 ``` 

 ## awkの編集結果をファイルにリダイレクトで出力して保存する 

 awkでリダイレクトを行う場合、たとえば「tail -F」などと組み合わせて利用する場合、単純に「>」で指定してもリダイレクトが行われない場合がある。 

 * 例:tail -F で「/work/test」というファイルを常時監視し、その内容に日付を付け足して「/work/test_log」ファイルに出力しようとしている。 

 ``` 
 $ tail -F /work/test | awk '{ print strftime("%Y/%m/%d %H:%M:%S") " " $0 }' > /work/test_log 
 ``` 

 tail -Fとawkを組み合わせてファイルに出力する場合、以下のようにリダイレクトの前に「{ system (" ")}」と記述する必要がある。 

 ``` 
 $ tail -F /work/test | awk '{ print strftime("%Y/%m/%d %H:%M:%S") " " $0 } { system (" ") }' > /work/test_log 
 ``` 

 ## Excelのフィルタのように、ファイルから〇〇以上、〇〇以下で行を抽出する 

 ``` 
 $ awk '列 >= 条件 && 列 <= 条件' 対象ファイル 
 $ awk '列 == 条件' 対象ファイル 
 $ awk '列 ~ /条件/ 対象ファイル' 
 ``` 

 ``` 
 $ cat test.txt  
 1 abc 10 
 2 def 15 
 3 ghi 0 
 $ awk '$3 >=5 && $3 <=10' test.txt 
 1 abc 10 
 $ awk '$2=="def"' test.txt 
 2 def 15 
 $ awk '$2 ~ /abc|ghi/' test.txt 
 1 abc 10 
 3 ghi 0 
 ``` 


 ## ◯◯時からXX時までの間のログを抽出 

 ``` 
 $ awk -F - '"Apr    5 14:30:00" < $1 && $1 <= "Apr    5 15:00:00"' /var/log/messages 
 ``` 

 ## grepのように行を抽出 

 ``` 
 $ awk '/文字列/' 対象ファイル 
 ``` 

 ## 任意の行を抽出 

 ``` 
 $ awk 'NR==行' 対象ファイル 
 $ awk 'NR==最初の行,NR==終わりの行' 対象ファイル 
 ``` 

 ## 文字列で範囲を指定して出力 

 ``` 
 $ awk '/任意の開始文字列/,/任意の終了文字列/' 対象ファイル 
 ``` 

 例えば、ファイル2カラム目の「00:00:02」~「00:00:04」までを抽出する場合 

 ``` 
 $ awk '$2 >= "00:00:02" && "00:00:04" >= $2' /tmp/sample.log 
 ``` 

 複数条件(1行目、文字列XXを含む行を抽出とか)で抽出する場合は、awk内でifを使用できる。 

 ``` 
 $ awk '{if(条件1||条件2) print}' 対象ファイル # or条件 
 $ awk '{if(条件1&&条件2) print}' 対象ファイル # and条件 
 ``` 









 # sed KnowHow 

 ## 基本的な使い方 

 ``` 
 $ sed 's/置換前文字列/置換後文字列/g' ファイルPATH 
 $ コマンド | sed 's/置換前文字列/置換後文字列/g' 
 ``` 

 ## ◯行目~◯行目にある特定の文字列を置換する場合 

 ``` 
 $ sed '4,8s/line/gyou/g' test.txt 
 ``` 

 ## 特定の文字列を含む行のみ置換する場合 

 ``` 
 $ sed '/検索文字列/s/置換前文字列/置換後文字列/g' 
 ``` 

 ## その行に表示される◯番目の文字列を置換する 

 ``` 
 $ sed 's/置換前文字列/置換後文字列/何文字目か' 
 ``` 

 ## マッチした文字列を再利用する 

 1個だけと複数指定可能な2つの方法がある 

 ### 1個だけの方法 

 置換後文字列の所に&を書くと、その部分がマッチした文字列に置き換わる。 

 ~~~ 
 $ echo "abc def hij klm" |sed -e 's/a[a-z]c/XXX & XXX/g' 
 XXX abc XXX def hij klm 
 ~~~ 

 ### 複数指定可能な方法 

 \\( と \\) で囲んだものがマッチした場合、順番に \1 \2 \3 と指定できる。 

 ~~~ 
 $ echo "abc def hij klm" |sed -e 's/^\([a-z][a-z][a-z]\) \(d[a-z][a-z]\) \([a-z][a-z][a-z]\) \([a-z][a-z][a-z]\)/XXX \4 \3 \2 \1 XXX/g' 
 XXX klm hij def abc XXX 
 ~~~ 

 ## 指定した行番号の箇所に行を挿入する 

 ``` 
 $ sed '◯i 挿入する行' 
 $ sed '4i testline' test.txt 
 ``` 

 ## 指定した行の後ろに行を挿入する 

 ``` 
 $ sed '◯a 挿入する行' 
 $ sed '4a testline' test.txt 
 ``` 

 ## 指定した複数の各行の前、後ろに行を挿入する 

 ``` 
 $ sed '◯,◯i 挿入する行' #前に挿入する場合 
 $ sed '◯,◯a 挿入する行' #後に挿入する場合 
 ``` 

 ## 指定したキーワードを持つ行の前・後に行を挿入する 

 ``` 
 $ sed '/キーワード/i 挿入する行' #前に挿入する場合 
 $ sed '/キーワード/a 挿入する行' #後に挿入する場 
 ``` 

 ## ◯行目~◯行目を削除する 

 ``` 
 $ sed '◯,◯d' 
 $ sed '1,4d' test.txt 
 ``` 

 ## 決まったキーワードを持つ行を除外する 

 ``` 
 $ sed '/キーワード/d' 
 $ sed '/line2/d' test.txt 
 ``` 

 ## ◯行目の内容を上書きする 

 ``` 
 $ sed '◯c 置き換え後の行' 
 $ sed '4c testline' test.txt 
 ``` 

 ## 特定のキーワードを持つ行を上書きする 

 ``` 
 $ sed '/キーワード/c 置き換え後の行' 
 $ sed '/line3/c testline' test.txt 
 ``` 

 ## ファイルの内容を上書きする 

 ``` 
 $ sed -i '置換条件' ファイルPATH 
 $ sed 's/line 1/line #/g' test.txt 
 ``` 

 ## 複数の置換条件を適用する 

 ``` 
 $ sed -e '置換条件' -e '置換条件' ... 
 $ sed -e 's/line/gyou/g' -e '5c aaaaaaa' test.txt 
 ``` 

 ## ファイルに書いた置換条件を読み込む 

 ``` 
 $ sed -f スクリプトファイルPATH 
 $ cat /root/sed_script 
 s/line/gyou/g 
 5c aaaaaaa 
 $ sed -f ./sed_script test.txt 
 ``` 

 ## 小文字→大文字の変換をする 

 ``` 
 $ sed 's/\(.*\)/\U\1/' 
 $ sed 's/\(.*\)/\U\1/' test.txt 
 ``` 

 ## 大文字→小文字の変換をする 

 ``` 
 $ sed 's/\(.*\)/\L\1/' 
 $ sed 's/\(.*\)/\L\1/' test.txt 
 ``` 

 ## ダブルコーテーション、シングルコーテーションに囲まれた文字列を抽出する 

 ~~~ 
 $ sed 's/^.*"\(.*\)".*$/\1/' # ダブルクォーテーションの場合 
 $ sed "s/^.*'\(.*\)'.*$/\1/" # シングルクォーテーションの場合 
 ~~~ 

 ちな、grepやawkでもできる 

 ~~~ 
 $ grep -Po '(?<=")[^",]+(?=")' # ダブルクォーテーション 
 $ grep -Po "(?<=')[^',]+(?=')" # シングルクォーテーション 
 $ awk -F'['\''"]' '{print $2}' 
 ~~~ 

 ## ダブルクォーテーションで囲まれた文字列に対して処理を行う 

 ``` 
 $ sed 's/"\([^"]*\)"/"置換後の値"/' 対象のファイルPATH 
 $ sed 's/"\([^"]*\)"/"replace"/' test.txt 
 ``` 

 ## シングルクォーテーションで囲まれた文字列に対して処理を行う 

 ``` 
 $ sed 's/"\([^"]*\)"/"置換後の値"/' 対象のファイルPATH 
 $    sed "s/'\([^']*\)'/'replace'/" test.txt 
 ``` 

 ## メールアドレスを『○○○@●●●●●』というようにマスキング置換する 

 ``` 
 $ sed "s/[^@ ]*@[^@]*\.[^@ ]*/○○○@●●●●●/g" ファイルPATH 
 ``` 

 ## sedで行頭の置換を指定する 

 ``` 
 $ sed 's/^置換前文字列/置換後文字列/g' ファイルPATH 
 $ sed 's/^test/aaaa/g' test.txt 
 ``` 

 ## sedで行頭以外の置換を指定する 

 ``` 
 $ sed 's/\([^^]\)置換前文字列/置換後文字列/g' ファイルPATH 
 $ sed 's/\([^^]\)test/aaaa/g' test.txt 
 ``` 

 ## 指定したディレクトリ内のファイルを再帰的に置換する 

 ``` 
 $ sed 's/置換前/置換後/g' $(find 対象のフォルダPATH -type f) 
 ``` 

 ## 特定の文字列~文字列間を置換する 

 ``` 
 $ sed '/文字列(開始)/,/文字列(終了)/s/○○○/●●●/g' 対象のファイルPATH 
 $ sed '/line2/,/line3/s/test/aaaa/g' test.txt    # line2とline3という文字の間にあるtestをaaaaに置換 
 $ sed '/line2/,/line3/caaaa' test.txt            # line2がある行とline3がある行、及びその間の行をaaaaに入れ替え 
 ``` 

 ## 日本語(マルチバイト文字)のみを置換する 

 ``` 
 $ LANG=C sed 's/[\x80-\xFF]//g' ファイルPATH 
 $ sed 's/[\x80-\xFF]/●/g' test.txt 
 ``` 












 # Other Command KnowHow 

 ## grep 

 ### or検索 

 ``` 
 $ grep -e 検索したい文字列1 -e 検索したい文字列2 検索したいテキストファイル 
 ``` 

 ### 大文字・小文字を区別しない 

 ``` 
 $ grep -i 検索したい文字列 検索したいテキストファイル 
 ``` 

 ### 検索にヒットする前後の行を出力 

 検索にヒットした前の行を出力する場合 

 ``` 
 $ grep 検索したい文字列 -B 出力したい行数 検索したいテキストファイル 
 ``` 

 検索にヒットした後の行を出力する場合 

 ``` 
 $ grep 検索したい文字列 -A 出力したい行数 検索したいテキストファイル 
 ``` 

 前後の行を同時に出力したい場合は、Cオプションを付与する。 

 ``` 
 $ grep 検索したい文字列 -C 出力したい行数 検索したいテキストファイル 
 ``` 

 ### 検索にヒットする行数を取得する 

 ``` 
 $ grep -c 検索したい文字列 検索したいテキストファイル 
 ``` 

 ### ファイルの行番号を出力する 

 ``` 
 $ grep -n 検索したい文字列 検索したいテキストファイル 
 ``` 

 ### 単語で検索する 

 例えばadで検索するとaddressやmadといった、別の意味を持つ単語も引っかかってしまうので 

 ``` 
 $ grep -w 検索したい文字列 検索したいファイル 
 ``` 

 ### 複数のキーワードをパターン化したファイルを読込み、合致した行を出力する 

 ``` 
 $ grep -f 検索パターンを記述したファイル 検索したいファイル 
 ``` 

 ### 検索キーワードを持つファイルのリストを取得する 

 ``` 
 $ grep -l 検索パターンを記述したファイル 検索したいフォルダ/* 
 ``` 

 ## find 

 ここ読んでよく勉強すること!!1 

 俺的備忘録 〜なんかいろいろ〜 - findコマンドで覚えておきたい使い方12個 
 http://orebibou.com/2015/03/find%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A6%9A%E3%81%88%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E4%BD%BF%E3%81%84%E6%96%B912%E5%80%8B/ 

 ## date 

 ### 今日を基準として、任意の時間経過された日時を求める 

 ``` 
 $ date 
 2015年 10月 31日 土曜日 21:29:13 JST 
 $ date -d '-1day' 
 2015年 10月 30日 金曜日 21:29:05 JST 
 $ date -d '1day' 
 2015年 11月    1日 日曜日 21:29:08 JST 
 $ date -d '1hour' 
 2015年 10月 31日 土曜日 22:31:33 JST 
 $ date -d '1month' 
 2015年 12月    1日 火曜日 21:31:38 JST 
 $ date -d '1year' 
 2016年 10月 31日 月曜日 21:31:42 JST 
 ``` 

 ### 来月の月初、3ヶ月先の月末日付を求める 

 ``` 
 $ date +%Y/%m/%d 
 2015/10/31 
 $ date +%Y/%m/01 
 2015/10/01 
 $ date +%Y/%m/01 -d '+1 month' 
 2015/12/01 
 $ date +%Y/%m/%d -d "-1day `date +%Y%m01 -d '+4 month'`" 
 2016/02/29 
 ``` 

 ### 閏年の判断 

 閏年じゃない場合はエラーになる。 

 ``` 
 $ date -d 2012-02-29 +%Y 
 2012 
 $ date -d 2013-02-29 +%Y 
 date: `2013-02-29' は無効な日付です 
 ``` 

 -f-とすれば標準入力から複数の日付を受け付けてくれる。 
 こうすれば閏年だけ抜き出せる。 

 ``` 
 $ seq 1900 2400 | sed 's/$/-02-29/' | date -f- +%Y 2> /dev/null 
 ``` 




 ## sort 

 ### バイト数(KB、MB、GB etc…)を並び替えする 

 ``` 
 $ du -h /home | sort -hr 
 ``` 


 ## uniq 

 ### 重複した行・重複していない行だけを表示させる 

 重複している行のみを出力させる場合は「-d」を、重複のない(ユニークな)行をのみを出力させる場合は「-u」オプションを付与する。 

 ``` 
 $ cat test.txt | uniq -d 
 $ cat test.txt | uniq -u 
 ``` 

 ## watch 

 ### 実行コマンドがエラーになった場合、watchコマンドを終了する 

 ``` 
 $ watch -e 実行コマンド 
 ``` 

 なお、「-e」の場合はコマンドの実行は停止するが何かキーを押下するまで画面はそのままとなる。 

 ### 実行コマンドに変化があった場合、watchコマンドを終了する 

 ``` 
 $ watch -g 実行コマンド 
 ``` 

 ## xargs 

 ### 基本的な使い方 

 以下のようにパイプでつなぐことで前のコマンド(コマンド1)で取得した値(標準出力)を利用してxargsで指定した別のコマンド(コマンド2)に引数として渡して実行させる事ができるコマンド 

 ``` 
 $ コマンド1 | xargs コマンド2 
 ``` 

 ### 実行されるコマンド内容を表示させる 

 ``` 
 $ コマンド1 | xargs -t コマンド2 
 ``` 

 ### コマンドライン一行に引数をいくつ渡すか指定する 

 ``` 
 $ コマンド1 | xargs -n 引数の数 コマンド2 
 ``` 

 ### 引数の値を明示的に利用する 

 ``` 
 $ コマンド1 | xargs -I{} コマンド2 {} 
 ``` 

 例えば「/work」フォルダ配下にあるファイルに対し、元のファイル名の後ろに「.bk」という文字列を付け足してコピーする場合 

 ``` 
 $ find /work -type f | xargs -t -I{} cp {} {}.bk 
 ``` 

 ### コマンドの実行をするかどうか確認する 

 ``` 
 $ コマンド1 | xargs -p コマンド2 
 ``` 

 ### 複数プロセスを同時に実行させる 

 ``` 
 $ コマンド1 | xargs -P 最大プロセス数 コマンド2 
 ``` 

 ### 引数の区切り文字を指定する 

 ``` 
 $ コマンド1 | xargs -d区切り文字 コマンド2 
 ``` 

 ## eval 

 ### 変数の2重展開 

 ``` 
 #!/bin/sh 
 # 変数 VARに値が入力されている 
 VAR="test" 
 
 # 変数 EVAL_VARに、変数名である「VAR」という文字列を入力 
 EVAL_VAR="VAR" 
 
 # 変数 EVAL_VARを呼び出す 
 # echo $EVAL_VAR 
 eval echo '$'$EVAL_VAL 
 ``` 

 ### 変数が配列の場合の2重展開 

 ``` 
 #!/bin/sh 
 # 変数「VAL」を配列として値を代入していく 
 VAL[0]="line 1" 
 VAL[1]="line 2" 
 VAL[2]="line 3" 
 
 # 変数 EVAL_VALに、変数名である「VAL」という文字列を入力 
 EVAL_VAL="VAL" 
 
 # 変数 EVAL_VALを呼び出す 
 eval echo '$'$EVAL_VAL 
 
 # 変数 EVAL_VALを配列として全行呼び出す 
 eval echo '${'$EVAL_VAL'[@]}' 
 
 # 変数 EVAL_VALを配列として[1]を呼び出す 
 eval echo '${'$EVAL_VAL'[1]}' 
 ``` 

 ## ssh 

 ### ローカルのbashの設定使ってssh接続 

 ``` 
 $ ssh -t user@host 'bash --rcfile <( echo ' $(cat ~/.bashrc ~/.bash_function_etc... | base64 ) ' | base64 -d)' 
 ``` 

 >環境によってはbase64に-w0オプションが必要かも? 

 ### Dockerにローカルのbashrcやvimrcを使って接続 

 ``` 
 $ docker run --rm -it コンテナ bash -c '/bin/bash --rcfile <(echo -e '$(cat ~/.bashrc ~/.bash_function_etc... |base64)'|base64 -d)' 
 ``` 

 今のセッションの環境変数をそのまま使う場合は 

 ``` 
 $ docker run --rm -it コンテナ bash -c '/bin/bash --rcfile <(echo -e '$(cat <(set) <(alias)|base64 -w0)'|base64 -d)' 
 ``` 

 ### ローカルにあるスクリプトを配布せずにリモート先で実行させる 

 ``` 
 $ ssh リモート先のユーザ名@リモート先のホスト名(IPアドレス) 'sh ' < 実行させたいスクリプトのパス 
 $  
 ``` 

 ### 直接リモート先のマシン上でコマンドを用いたsedを行わせる 

 ``` 
 $ sed ユーザ名@リモートホスト sed s/aaaaa/`hostname`/g 対象ファイル     # ローカル側でhostnameが実行される 
 $ sed ユーザ名@リモートホスト 'sed s/aaaaa/`hostname`/g' 対象ファイル # リモート側でhostnameが実行される 
 ``` 

 ### リモート側のファイルにローカルファイルの内容を追記する方法 

 ``` 
 $ ssh ユーザ名@リモートホスト "cat >> /追記するリモートファイルのパス" < /追記させるローカルファイルのパス 
 $ ssh test@192.168.0.240 "cat >> /tmp/test_remote.text" < test_local.txt 
 ``` 

 ローカル側のコマンドの実行結果(ローカル側のファイルに対し、sedを実行した後の内容を追記するなど)を追記する場合は、以下のようにする。 

 ``` 
 $ ローカル側で標準出力を行うコマンド | ssh ユーザ名@リモートホスト "cat >> /追記するリモートファイルのパス" 
 $ sed 's/test/AAAA/g' text_local.txt | ssh test@192.168.0.240 " 
 ``` 

 ### ssh経由でディレクトリにあるファイル一覧をdiffする 

 公開鍵認証じゃないとダメかもしれない 

 ``` 
 $ diff <(ssh ユーザ名@ホスト名 'find /確認するPATH -type f | sort') <(find /確認するPATH -type f | sort) 
 $ diff <(ssh test@192.168.0.240 find /work -type f | sort) <(find /work -type f | sort) 
 ``` 

 rsyncコマンドで代用できる 

 ``` 
 $ rsync --checksum --dry-run -rvce "ssh -p ポート番号" ユーザ名@ホスト名:/対象ディレクトリ /対象ディレクトリ 
 ``` 

 ### ssh経由でファイル内容をdiffする 

 ``` 
 $ diff <(ssh ユーザ名@ホスト名 'cd /確認するPATH; grep -Rn "" ./ | sort') <(cd /確認するPATH; grep -Rn "" ./ | sort) 
 $ diff <(ssh test@192.168.0.240 'cd /work;grep -Rn "" ./ | sort') <(cd /work;grep -Rn "" ./ | sort) 
 ``` 

 ### ローカルでファイル・フォルダを圧縮し、リモートでアーカイブファイルとして保持させる 

 ``` 
 $ tar zcvf - /アーカイブ化したいディレクトリのパス | ssh ユーザ名@リモートホスト "cat > /リモート側で作成するアーカイブ・ファイルのパス 
 $ tar zcvf - /wort_test | ssh test@192.168.0.240 "cat > /wort_test.tar.gz" 
 ``` 

 ### ローカルのアーカイブファイルをリモートで解凍する 

 ``` 
 $ ssh ユーザ名@リモートホスト "tar zxvf -C リモート側で展開させたいフォルダ -" < /リモート側で解凍させたいアーカイブファイルのパス 
 $ ssh test@192.168.0.240 "tar zxvf - -C /test1234" < test.tar.gz 
 ``` 

 ### リモートでファイル・フォルダを圧縮し、ローカルでアーカイブファイルとして保持させる 

 ``` 
 $ ssh ユーザ名@リモートホスト "tar -cf - /アーカイブ化したいディレクトリのパス" | gzip > /ローカル側で作成するアーカイブ・ファイルのパス 
 $ ssh test@192.168.0.240 "tar -cf - /work/test" | gzip > /root/test.tar.gz 
 ``` 

 ### リモートのアーカイブファイルをローカルで解凍する 

 ``` 
 $ ssh ユーザ名@リモートホスト "cat /ローカル側で解凍させたいアーカイブファイル" | tar zxvf - -C ローカル側で展開させたいフォルダ 
 $ ssh test@192.168.0.240 "cat /work.tar.gz" | tar zxvf - -C /test/ 
 ``` 

 ### 踏み台サーバ経由でログインを行う 

 ``` 
 $ ssh ユーザ名@接続先のホスト名(IPアドレス) -o 'ProxyCommand ssh 踏み台サーバのユーザ@踏み台サーバのホスト名 nc %h %p' 
 $ ssh test1@192.168.0.2-o 'ProxyCommand ssh test2@192.168.0.3 nc %h %p' 
 ``` 

 ### ssh接続時に直接コマンドを実行する 

 ``` 
 $ ssh ユーザ名@接続先のホスト名(IPアドレス) '接続先で実行させるコマンド' 
 ``` 

 ### sshでsuによるユーザ切り替え時に自動でパスワード入力をさせる 

 ``` 
 $ ssh login@host "su - user -c 'command' << EOF 
 password 
 EOF" 
 ``` 

 ## dd 

 ### バイナリファイルでのcutコマンド相当 

 ``` 
 $ cat file | dd bs=1 skip=100 
 ``` 

 ### バイナリファイルでのheadコマンド相当 

 固定長レコード限定 

 ``` 
 $ dd bs=[レコード長] count=[抜き出したい行数] skip=[読み飛ばしたい行数] if=[入力ファイル] of=[出力ファイル] 
 ``` 






 # シェル芸 

 UPS友の会 
 https://www.usptomo.com/ 

 上田ブログ 
 https://blog.ueda.asia/ 

 シェル芸人達 
 https://daichkr.hatelabo.jp/collection/960679194075891200 

 日々是迷歩 
 http://papiro.hatenablog.jp/ 

 slideshare シェル芸 
 http://www.slideshare.net/search/slideshow?searchfrom=header&q=%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8&ud=&ft=&lang=&sort= 

 ## Open usp Tukubai 

 基本的に、以下からダウンロードして展開すれば使える 
 https://uec.usp-lab.com/TUKUBAI/CGI/TUKUBAI.CGI?POMPA=DOWNLOAD 

 コマンドの説明は以下 
 https://uec.usp-lab.com/TUKUBAI_MAN/CGI/TUKUBAI_MAN.CGI?POMPA=LIST 

 Github - ShellShoccar-jpn/Parsrs 
 https://github.com/ShellShoccar-jpn/Parsrs 

 ## egzact 

 シェルの弱点を補おう!"まさに"なCLIツール、egzact 
 http://qiita.com/greymd/items/3515869d9ed2a1a61a49 https://github.com/greymd/egzact 

 ## エクシェル芸 

 俺的備忘録 〜なんかいろいろ〜 - Excelファイル(~.xls/~.xlsx)をLinuxコンソール上でCSV方式に変換する方法 
 https://orebibou.com/2016/12/excel%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%ef%bd%9e-xls%ef%bd%9e-xlsx%e3%82%92linux%e3%82%b3%e3%83%b3%e3%82%bd%e3%83%bc%e3%83%ab%e4%b8%8a%e3%81%a7csv%e6%96%b9%e5%bc%8f%e3%81%ab%e5%a4%89%e6%8f%9b/ 








 ## ワンライナー 

 ### アクセスログから、接続元IPをカウントし、合計で100回以上アクセスしているIPのみアクセスが多い順に表示する 

 ``` 
 cat /var/log/httpd/access_log* |     awk '/\"GET .*\" 200 / || /\"CONNECT .*\" 200 /     {cnt[$1]+=1};END{for (key in cnt)    {if(cnt[key] > 100)    {print cnt[key],key}}}' |    sort -nr 
 ``` 

 ### アクセスログから、リファラなしの閲覧先ドメインをカウントし、合計で100回以上アクセスがあるURLのみアクセスが多い順に表示する 

 ``` 
 cat /var/log/httpd/access_log*    |    awk -F\" '/GET .* 200 / || /CONNECT .* 200 /    {cnt[$2]+=1};END{for (key in cnt)    {if(cnt[key] > 100)    {print cnt[key],key}}}' |    sed -e 's% HTTP/...%%g' | sort -nr 
 ``` 



 # シェル芸化 

 シェル芸の定義はこうだ。 

 >マウスも使わず、ソースコードも残さず、GUI ツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI 端末へのコマンド入力一撃で終わらすこと。 

 では、やってみましょう。 
 全ての手続きを1行に。 

 ## 1行化の基本 

 ``` 
 $ echo "a" 
 $ echo "b" 
 $ echo "c" 
 ``` 

 ;で区切ることで1行化できる。 

 ``` 
 $ echo "a";echo "b";echo "c" 
 ``` 

 ### && の利用 

 ``` 
 $ hoge ; date 
 -bash: hoge: コマンドが見つかりません 
 2016年    6月 19日 日曜日 11:01:32 JST 
 ``` 

 &&で区切ると、hogeが正常終了した場合のみ、後続のコマンドを実行する。 
 正常終了とは、コマンドの復帰値($?)が0の場合を指す。 

 ``` 
 $ hoge && date 
 -bash: hoge: コマンドが見つかりません 
 ``` 

 これは以下と等価である。 

 ``` 
 #!/bin/bash 

 hoge 

 RET=$? 
 if [ $RET -eq "0" ] 
 then 
   date 
 else 
   exit $RET 
 fi 
 ``` 

 ### || の利用 

 ``` 
 $ hoge ; date 
 -bash: hoge: コマンドが見つかりません 
 2016年    6月 19日 日曜日 11:01:32 JST 
 ``` 

 ||で区切ると、hogeが異常終了した場合のみ、後続のコマンドを実行する。 
 異常終了とは、コマンドの復帰値($?)が0以外の場合を指す。 

 ``` 
 $ hoge || date 
 -bash: hoge: コマンドが見つかりません 
 2016年    6月 19日 日曜日 11:17:16 JST 
 ``` 

 これは以下と等価である。 

 ``` 
 #!/bin/bash 

 hoge 

 RET=$? 
 if [ $RET -nq "0" ] 
 then 
   date 
 fi 
 ``` 

 ## if文の1行化 

 これが 

 ``` 
 RET="0" 
 if [ $RET -eq "0" ] 
 then 
   echo "true" 
 else 
   echo "false" 
 fi 
 ``` 

 こうじゃ! 

 ``` 
 RET="0";if [ $RET -eq "0" ]; then echo "true"; else echo "false"; fi 
 ``` 
 RETの値を変えれば、分岐されていることを確認できる。 

 ### && , || の利用 

 以下のように、&& や || を利用することもできます。 

 ``` 
 $ RET="0";[ $RET -eq "1" ] || echo "It work" 
 It work 
 $ RET="0";[ $RET -eq "0" ] && echo "It work" 
 It work 
 ``` 

 ## for文の1行化 

 これが 

 ``` 
 for I in {1..10} 
 do 
   echo $I 
 done 
 ``` 

 こうじゃ! 

 ``` 
 for I in {1..10} ; do echo $I ; done 
 ``` 

 これでもいけるらしい 

 ~~~ 
 for I in {1..5};{ echo $I;} 
 for((;;)){ echo 1;sleep 1; } # 無限ループ 
 ~~~ 

 ## while文の1行化 

 これが 

 ``` 
 I=1 
 while [ $I -le 10 ] 
 do 
   echo $I 
   I=$((I+1)) 
 done 
 ``` 

 こうじゃ! 

 ``` 
 I=1; while [ $I -le 10 ]; do echo $I;I=$((I+1)); done 
 ``` 

 ## case文の1行化 

 これが 

 ``` 
 case $I in 
   a ) echo "a1" 
       echo "a2";; 
   b ) echo "b1" 
       echo "b2";; 
   * ) echo "default";; 
 esac 
 ``` 

 こうじゃ! 

 ``` 
 case $I in a ) echo "a1"; echo "a2";; b ) echo "b1"; echo "b2";; * ) echo "default";; esac 
 ``` 

 ## 関数の1行化 

 これが 

 ``` 
 funcSample(){ 
 echo "It work" 
 } 
 funcSample 
 ``` 

 こうじゃ! 

 ``` 
 funcSample(){ echo "It work"; };funcSample 
 ``` 

 ## シェル芸ブブ化 

 シェルスクリプトをシェル芸化(ワンライナー)するスクリプト 

 [[シェル芸ブブ化]] 



 # Test/Lint 

 POSTD - Bashアプリケーションをテストする 
 http://postd.cc/bash%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%82%92%e3%83%86%e3%82%b9%e3%83%88%e3%81%99%e3%82%8b/ 

 ShellCheck - ShellCheck, a static analysis tool for shell scripts 
 http://www.shellcheck.net/ https://github.com/koalaman/shellcheck 

 Batsを使ったシェルスクリプトのテスト 
 https://rcmdnk.com/blog/2019/10/11/computer-github-bash/ 

 pre-commitでShellCheckを使う 
 https://rcmdnk.com/blog/2023/01/09/computer-shell-python/ 

 AWK commands equivalent to SQL query – Data manipulation 
 https://subhadip.ca/unixlinux/awk-commands-equivalent-to-sql-query-data-manipulation/4/ 

 >SQLのINNER JOINやLEFT OUTER JOINをawkで実現 

 シェルスクリプトにxUnitを使ってみる 
 https://qiita.com/filunK/items/aa067383aaa317594d17 






 # Memo 

 ## sed 

 Qiita - sedのパターンスペース・ホールドスペースの動作を図で学ぶ 
 http://qiita.com/gin_135/items/773fec1343a69c9f90d6 

 俺的備忘録 〜なんかいろいろ〜 - sedのデバッグやHTML化ができる『sedsed』 
 https://orebibou.com/2016/11/sed%E3%81%AE%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%82%84html%E5%8C%96%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%8Esedsed%E3%80%8F/ 


 ## Misc 

 Defensive BASH Programming 
 http://www.kfirlavi.com/blog/2012/11/14/defensive-bash-programming 

 Perl1行野郎 
 http://web.archive.org/web/20020203071153/www13.cds.ne.jp/~ohsaru/perl/oneline.html 

 WICKED COOL SHELL SCRIPTS 
 http://www.intuitive.com/wicked/wicked-cool-shell-script-library.shtml 

 Bourne Shell自習テキスト 
 http://lagendra.s.kanazawa-u.ac.jp/ogurisu/manuals/sh-text/sh/ 

 Qiita - richmikan@github 
 http://qiita.com/richmikan@github 

 glot.io - 様々な言語のコードスニペットが共有できるサイト 
 https://glot.io/ 

 CMD-CHALLENGE - なんかCTFのようなシェル芸勉強サイト!! 
 https://cmdchallenge.com/ 

 Thanks Driven Life - shellcheck を Docker で実行する 
 http://gongo.hatenablog.com/entry/2017/03/28/223757 

 Command Challenge - ★CTFみたい!!★ 
 https://cmdchallenge.com/ 

 explainshell.com - 難しめのシェル芸の解析 
 https://explainshell.com/ 

 Yakst - 私が他人のシェルスクリプトから学んだこと 
 https://yakst.com/ja/posts/31 

 Qiita - bashの組込みコマンド自作によるスクリプトの高速化 
 https://qiita.com/satoru_takeuchi/items/7d424fa5ef1e33ace4df 

 Qiita - ネットワーク越しでパイプしたり、あらゆるデバイス間でデータ転送したい! 
 https://qiita.com/nwtgck/items/78309fc529da7776cba0 

 ble - Bashを動的にシンタックスハイライトする 
 https://github.com/akinomyoga/ble.sh 

 if 文と test コマンド | UNIX & Linux コマンド・シェルスクリプト リファレンス 
 https://shellscript.sunone.me/if_and_test.html 

 bashスクリプティング研修の資料を公開します 
 https://www.m3tech.blog/entry/2018/08/21/bash-scripting 

 set -eのもとで特定のコマンドの終了ステータスを変数に入れるシェルスクリプトのスニペット 
 https://gfx.hatenablog.com/entry/2021/12/15/153937 

 bash スクリプトの実行中上書き動作について 
 https://zenn.dev/mattn/articles/5af86b61004bdc 

 pure sh bible (📖 A collection of pure POSIX sh alternatives to external processes). - コマンドを使わずpure bashであれやこれやする一覧 
 https://github.com/dylanaraps/pure-bash-bible#get-the-directory-name-of-a-file-path 

 shell.how - インタラクティブにコマンド内容やオプションを解説してくれる 
 https://www.shell.how/ 

 なぜシェルスクリプトで高度なデータ管理にSQLiteを使うべきなのか? ~ UNIX/POSIXコマンドの欠点をSQLで解決する 
 https://qiita.com/ko1nksm/items/33ab7ced0f9f8acdff28 

 onceupon/Bash-Oneliner 
 https://github.com/onceupon/Bash-Oneliner 

 シェルスクリプトの表示を豊かにするgumの使い方 
 https://dev.classmethod.jp/articles/eetann-gum-suburi/ 

 GoogleのShell Style Guideの邦訳 
 https://qiita.com/yabeenico/items/72b904d4bb0b6d732a86 

 シェルスクリプトでgetoptsで解析する引数をポジショナルな引数と混合して使えるようにする 
 https://rcmdnk.com/blog/2023/11/01/computer-bash/ 

 Bashの文字列で特殊文字を使う方法 
 https://azisava.sakura.ne.jp/programming/0010.html 

 シェルスクリプトで関数をそのままサブコマンドとして使う 
 https://rcmdnk.com/blog/2024/09/08/computer-bash/#google_vignette 

 改めて整理する、コンソール・ターミナル・仮想コンソール・端末エミュレータ・擬似端末 
 https://www.kanzennirikaisita.com/posts/what-is-console-terminal-etc 

 ASCII control characters in my terminal (端末の ASCII 制御文字) 
 https://jvns.ca/blog/2024/10/31/ascii-control-characters/ 

 yassinebenaid/bunster(シェルスクリプトをバイナリ形式に変換するツール:Go実装にトランスパイル) 
 https://github.com/yassinebenaid/bunster 



 # 謝辞 

 以下のサイトを中心に参考にさせて頂いております。 
 ありがとうございます。 

 日々是迷歩 
 http://papiro.hatenablog.jp/ 

 俺的備忘録 〜なんかいろいろ〜 
 http://orebibou.com/ 

 Qiita - 実用 awk ワンライナー 
 http://qiita.com/b4b4r07/items/45d34a434f05aa896d69 

 SlideShare - 検索:シェル芸 
 http://www.slideshare.net/search/slideshow?searchfrom=header&q=%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8