Webフロントエンドハイパフォーマンスチューニングの読書メモ
Webフロントエンド ハイパフォーマンス チューニングの読書メモ。
Webブラウザのレンダリングエンジン、JavaScriptエンジンについて
ブラウザ | レンダリングエンジン | JavaScriptエンジン |
---|---|---|
Chromium (Chrome, Opera, 次世代 Edge) | Blink | V8 |
Safari | WebKit | JavaScript Coer |
Firefox | Gecko | SpiderMonkey |
Edge | EdgeHTML | Chakra |
IE | Trident | Chakra |
リソースの取得に用いるネットワークプロトコル
IP
ネットワークのノード間のパケットのやり取りを中継するプロトコル。
ホスト名の解決に DNS、リソースの取得には HTTP が用いられます。これらはどちらも IP 上で利用されます。
TCP
TCP はコネクションとい概念を持ちます。相手先の IP アドレスとポート番号を指定して接続を確立し、一度接続を確立すると接続が終了するまで、IP と比べると信頼性のあるデータ転送を行えます。
ウェブにおいて、HTTP はこの TCP の上で利用されます。したがって、HTTP でリソースを取得する前に必ず TCP 接続を確立します。
DNS
ホスト名を IP アドレスを変換する作業は、クライアント内の DNS リゾルバを通じて行います。DNS リゾルバは自身のキャッシュや DNS サーバに問い合わせることでホスト名に紐づく IP アドレスを検索します。
JavaScript による計測
- Navigation Timing API による計測
- User Timing API による計測
- Resource Timing API による計測
- Frame Timing APIによる計測
- Server Timing APIによる計測
- High Resolution Time APIによる計測
- Perfomance Observer による継続的な計測
Navigation Timing API による計測
ブラウザがページをロードするのに要した処理時間の内訳を知ることができる。
- window.performance.timing オブジェクト
- window.performance.navigation オブジェクト
User Timing API による計測
開発者が任意の処理にかかる時間を計測することができる。
Resource Timing API による計測
リソースの取得にかかっている時間の統計情報を得られる。
Navigation Timing API では、ユーザーがどれぐらいブラウザのナビゲーション処理に待たされているかを詳細に知ることができる。ただし、個別のリソースの取得にどう時間がかかっているかについては分からない。
※ Network パネルで見ることができるリソース取得のパフォーマンスの詳細情報とほとんど同じもの。
Frame Timing API による計測
Frame Timing API
はページ描画のフレーム情報を取得するAPI。
Navigation Timing API
やResource Timing API
はネットワーク処理に関するパフォーマンスの情報だが、これはレンダリングパフォーマンスの情報ということになる。
Server Timing API による計測
Server Timing API
は、レスポンスヘッダのServer-Timing
フィールドにサーバーの各処理にかかった時間を含めて、クライアントでそれを参照できるようにしようというAPI。
High Resolution Time API による計測
High Resolution Time API
はマイクロ秒単位でパフォーマンスを計測するためのAPI。
Date.now()のミリ秒では不十分ということで、細かな計測にはperformance.now()を使うと良い。
Perdoemance Observerによる継続的な計測
ブラウザパフォーマンスを計測するAPIは以下の5つがある。
Performance Observerはこれらの変更を監視するAPI。
Javascript の実行のチューニング
- Page Visibility API
高度なチューニング
ブラウザのレンダリングは4つの工程から成る。
① Loading -> ② Scripting -> ③ Rendering -> ④ Painting
さらに、各工程はそれぞれ以下の処理に細分化される。
- ① Loading
- Download
- Parse
- ② Scripting
- ③ Rendering
- CalculateStyle
- Layout
- ④ Painting
- Paint
- Rasterize
- CompositeLayers
① Loading
省略。
② Scripting
省略。
③ Rendering
Calculate style
DOM 要素と CSS ルールを突き合わせて要素ごとのスタイル情報を計算する。
DOM ツリーの構造を変化させたり、DOM 要素の属性を変化させると Calculate Style が引き起こされる。
- DOM ツリーの構造の変化ってなに?
appendChild()メソッドや removeChild()メソッド、innerHTML プロパティや textContent プロパティなどを使って DOM ツリーに変化を加えること。
DOM 要素の属性ってなに?
- class 属性や id 属性などを変更すること。
アニメーション中にこういうった変更を行う理由はないので避けてください。ある要素の CSS プロパティを変化させたい場合には、 Javascript で次のようにDOM要素をstyle プロパティから直接変更することで DOM 要素と CSS ルールのマッチング処理を避けることができます。
element.style.opacity = '0.8';
Layout
視覚的要素のレイアウト情報を計算する
DOM ツリーの構造を変化させたり、コンテンツを変化させると Layout 処理が引き起こされる。
また、レイアウトの更新を必要とする CSS プロパティを変更すると、Layout 処理が引き起こされます。
Layout 処理が引き起こされると、後続の Paint 処理や Composite 処理もすべて引き起こされます。
Layout を引き起こさずに要素の位置や大きさを変えたい場合には、transform CSS プロパティの値に translate()や scale()を使ってください。
// だめ element.style.marginTop = '10px'; element.style.left = '100px'; // よい element.style.transform = 'translate(100px, 10px)';
④ Painting
視覚的要素をレイヤーごとにビットマップ化する。
Paint は、内部で生成されたレイヤーごとに描画命令を生成する処理です。 Paint が引き起こされると、皇族の Resterize と Composite Kayers も実行される。
Layout 処理が引き起こされた場合や、再度 Resterize が必要となる CSS プロパティを変更すると引き起こされます。
ペイントを引き起こす CSS プロパティ
- color
- border-color
- backgorund-color
- backgorund-image
- z-index
ペイントを引き起こさない CSS プロパティ
- opacity
- transform
原理的にそれが難しい場合、たとえば要素の色を変更したり背景画像を動かしたりするようなアニメーションの場合はここで最適化をストップさせることになる。
Composite Layers
描画されたレイヤーを合成して最終的なレンダリング結果を生成する。
translateZ ハックを使用してレイヤーの合成に GPU を使うと最適化できる。
.target { transform: translateZ(0); }
このハックを適用した上でアニメーションを行うとほとんんど GPU でのみ描画される高速なアニメーションを実装できる
var element = document.getElementByID('animation-target'); // アニメーションを始める前にGPUで合成されるレイヤーを生成する、 element.style.transform = 'translateZ(0)'; var i = 0; var loop = function() { // アニメーション中にもGPUでの合成が解除されないようにtranslateZ(0)をつける。 requestAnimationFrame(function() { element.style.transform = 'translateZ(0) translate2d(' + (i++) + 'px,0px'); loop(); }); }; loop();
これらの再レンダリング時のフェーズを減らすことは非常に重要。
参考文献
ブラウザレンダリングを理解するため簡単にまとめてみた - Qiita
シェルスクリプトまとめ2
定期的に行う作業を自動実行
crontab
・・・「crond に実行してほしいコマンドとその実行日時のセット(cronjob)」を管理するコマンド
cronjob を一覧表示する
$ sudo crontab -l
シェルスクリプトを実行するときは、ホームディレクトリからの相対パスか、絶対パスを記述する
$ crontab -e
MAILTO=chino@gmail.com # cronjobの実行結果が指定のメールアドレスに悪られる.(MTAが設定済みの場合) 45 11 * * 1 /home/chino/scripts/analyze_log.sh > /tmp/result_$(date +%Y-%m-%d).txt
公開鍵認証
公開鍵の登録
公開鍵の登録 = 公開鍵のファイルを所定の位置に置くこと
公開鍵のファイルをサーバ上にコピーして、その鍵ペアで認証したいユーザのホームの直下の.ssh
ディレクトリにauthorized_keys
という名前で置く。
$ ssh-keygen
$ scp id_rsa.pub chino@192.168.1.2:/tmp/
$ ssh chino@192.168.1.2
$ sudo mkdir -p ~chiya/.ssh/
$ sudo mv /tmp/id_rsa ~chiya/.ssh/authorized_keys # 認証したユーザ以外では読み書きできないように「.ssh」ディレクトリと「authorized_keys」のパーミッションを設定すれば、公開鍵の登録は完了。
$ sudo chown -R chiya:chiya ~chiya/.ssh/ # 所有者をそのユーザに、グループもそのユーザのグループに設定する。
$ sudo chmod 700 ~chiya/.ssh # 所有者のみに読み書き・閲覧を許可
$ sudo chmod 600 ~chiya/.ssh/authorized_keys # 所有者のみに読み書きを許可
公開鍵を簡単に登録したい
$ ssh-copy-id -i ~/.ssh/id_rsa.pub chino@192.168.1.2 # 公開鍵を登録するサーバ側のユーザを明示的に指定する。
-i
: 登録したい公開鍵のパスを指定する。
ログインに成功すると、サーバ上のユーザのauthorized_keys
に公開鍵が登録される。
※ パスワード認証化、登録済みの鍵ペアでそのユーザとしてログインできることが前提。
定時処理で自動的に scp したい
パスフレーズを設定しない。その代わりに実行に制限を与える。
$ scp ~/.ssh/logdl.pub chino@192.168.1.2:/tmp/
$ ssh chino@192.168.1.2
$ cat /tmp/logdl.pub >> ~/.ssh/authorized_keys
登録が完了したら設定を変更する。
$ vim ~/.ssh/authorized_keys
公開鍵の行の先頭に関連付けるコマンド列を追記する。
command="scp -f /var/log/nginx/access.log"
ファイルをダウンロードするとき
$ scp -f サーバ上のファイルのパス
ファイルをアップロードするとき
$ scp -t サーバ上のファイルのパス
試す。
$ scp -i ~/.ssh/logdl chino@192.168.1.2:/var/log/nginx/access.log /tmp/
#!/bin/bash server_log=/var/log/nginx/access.log key=~/.ssh/logdl servers="192.168.1.51 192.168.1.52 192.168.1.53" for server in servers do log=/tmp/${server}-access.log result=/tmp/${server}-result.csv scp -i $key chino@${server}:${server_log} $log ~/scripts/analyze_access.sh $log $result done
※ この方法は、簡単に設定できるが融通が利かない。scp の実行内容を変えるときはその都度、authorized_keys を修正しないといけない。パスが異なるファイルをいくつもダウンロードしたいときも、その分だけ鍵ペアを用意しないといけない。
複数のサーバのファイルを効率よく収集したい
よくわからん。
条件に当てはまるログの行数を収集したい
ページごとのアクセス数ではなく合計が求めたい。→ wc
を使おう
行数
$ cat /tmp/access.log | wc -l
単語数
$ cat /tmp/access.log | wc -w
文字数
$ cat /tmp/access.log | wc -m
grep で見つかった業の数
$ zcat /oldlog/2018/accesslog.log.1.gz | grep "/campaign" | wc -l
複数 ver
#!/bin/bash for lof in /oldlog/2018/accesslog.*.gz do zcat $log | grep "/campaign" | wc -l # zcat $log | grep "Jan/2018" | wc -l # 1月のログだけ # zcat $log | grep -E "([23][0-9]/Jan|0[0-9]|10/Feb)/2018" | wc -l # 2018年1月20から2月10日まで。複数の月をまたがるような日付の範囲は正規表現を使う。 done
#!/bin/bash total_count=0 for lof in /oldlog/2018/accesslog.*.gz do pattern="([23][0-9]/Jan|0[0-9]|10/Feb)/2018" count=$(zcat $log | grep "/campaign" | grep -E $pattern | wc -l) total_count=$(($total_count + $count)) # 算術展開。bashで計算機能を使うときは$(())と書く done echo "合計:${total_count}"
複数のテキストファイルを一括編集したい
sed
を使う。→ テキストを編集するツールの一種。ストリームエディタ。
$ sed -e "s/置換前の文字列/置換後の文字列/"
例
$ cat rabbithouse-coffee.csv | sed -e "s/購入済み/使用済み" > rabbithouse-coffee.csv.updated
※ 1 つのコマンド列の中で「ファイルを開く操作」と「そのファイルに書き込む操作」を同時にやると、ファイルが消失する。
#!/bin/bash source=/tmp/rabbithouse-coffee.csv backup=${source}.$(date +%Y-%m-%d).bak mv $source $backup cat $backup | sed -e "s/購入済み/使用済み/" > $source
正規表現を使って複数のテキストファイルを一括編集したい。
なるほど、分からん。
数字をまとめて
"s/category([0-9]+/category-\1-/"
category100a → category-100-a
category200c → category-200-c
否定形
"s#(://[^:]+)3000/#\1:13000/#"
スラッシュを正規表現の中に含めるので区切り文字にはスラッシュ以外の文字を使うとよい。
http://192.168.1.15:3000/ → http://192.168.1.15:13000/
http://rabbit-house:3000/ → http://rabbit-house:13000/
先頭だけ
字下げの深さが違う場合もまとめて置換
"s/^( +)* /\1!! /"
* コーヒー * キリマンジャロ * ブルーマウンテン * カプチーノ * 軽食 * サンドイッチ * オムライス * ハンバーグセット \* ライス/パン
↓
!! コーヒー !! キリマンジャロ !! ブルーマウンテン !! カプチーノ !! 軽食 !! サンドイッチ !! オムライス !! ハンバーグセット \* ライス/パン
prettier のせいで勝手に整形される・・・
末尾
句点があったら、その後に半角スペースを 2 つ追加する。
"s/。$/。 /"
古い日付のファイルを探して消したい
find
コマンドを使えばいい。ファイルを探すときはfind
。
1 ヶ月以上前のファイルの一覧を得る
$ find /backup/daily -ctime +30
-ctime
: 最終変更日(changed time)でファイルを探す。名前やパーミッション、ディレクトリの移動といった、あらゆる変更についての最終変更日時。
-atime
: 最終アクセス日時。誰からも長いこと参照されていないファイルを探す時に使う。
-mtime
: 最終変更日時。ファイルの内容が変更された日時。ファイルの内容の変更についてだけの最終変更日時。
大抵の場合は、-ctime
で事足りる。
#!/bin/bash remove_files="$(find /backup/daily -ctime +30)" for file in $remove_files do rm "$file" done
+
と-
の考え方
今日から出発して、まず指定の日数分だけ時間を遡るイメージ。
+
なら過去方向に進みながら古いファイルを探していく
-
なら今日(未来の方向)に向かって戻りながら新しいファイルを探していく
最終変更日が 1 年以上前の古いファイル
$ find ./ -ctime +365
最終変更日が 1 週間以内である(この一週間以内に変更された)新しいファイル
$ find ./ -ctime -8
最後に実行してから 1 週間前から 2 週間前までのファイル
$ find /logs/ -ctime +7 and -ctime -15
最後に実行してから半年前の、名前に「access」を含んだファイル
$ find /logs/ -ctime +180 and -name "*access*"
最後に実行してから 30 日前の、名前に「access」または「error」を含んだファイル
$ find /logs/ -ctime +30 and \( -name "*access*" -or -name "*error*" \)
ディスクが満杯になる前にファイルを削除したい
#!/bin/bash # 見出し行を除いた数値の部分だけ取り出す free_size=$(df /data/backup | sed -r -e "s/[^ ]+ +[^ ]+ +[^ ]+ +([^ ]+).+/\1/" | tail -n 1) required_size=$((10 * 1000 * 1000)) # 10GB if [ $free_size -lt $required_size] then files=$(find /data/backup -ctime +30) for file in $files do rm "$file" done fi
前のコマンドが完了したら次のコマンドを実行する
prepare-data && process-data && report-result
プログラムをソースからビルドしてインストールするとき
$ ./configure --prefix=$HOME/local/ && make && make install
最新の情報に基づいてパッケージを更新するとき
$ sudo apt-get install update && sudo apt-get upgrade && sudo apt-get clean
前のコマンドが失敗したら次のコマンドを実行する
$ download-data --server=primary.datastore || download-data --server=secondary.datastore || exit 1
サブシェル
サブシェル・・・シェルの分身を作って、そっちでコマンドを実行する。
#!/bin/bash cd /shared/ for dir in logs data users do (cd $dir; files=$(find ./ -name "*.bak"); for file in files; do rm "$file"; done) done
※ ;を末尾に付与してかっこの中を「コマンドを列挙したリスト」にする
※ 複数行に渡る構文を含んだ処理は、関数としてくくりだしておけばサブシェルの中でも簡単に使える。
#!/bin/bash cd /shared/ remove_files() { files=$(find ./ -name "*.bak") for file in files do rm "$file" done } for dir in logs data users do (cd $dir && remove_files) done
1 つ前の作業ディレクトリに戻る
$ cd -
case
#!/bin/bash do_at_rabbithouse() { # } # (ry case "$(hostname)" in rabbithouse) do_at_rabbithouse ;; flulu_de_rapan) do_at_flulu_de_rapan ;; amausaan) do_at_amausaan ;; starbucks*) # 正規表現も使える e.g. starbucks_01, starbucks_02 do_at_starbucks ;; doutor*|exe*) # 複数のパターンを列挙できる do_at_coffee_chain ;; esac
一定時間繰り返したい
#!/bin/bash count=0 while [ $count != 3600 ] do curl "http://..." sleep 1 # 1秒待つ。 sleep 0.5 なら0.5秒、sleep 30d なら30日間待つ count=$(($count + 1)) done
コマンドのすべての出力をログファイルに保存したい
入力は標準入力だけだけど、出力は 2 つある。
標準出力・・・全般的な出力用(1 番)。1>と書く。>は 1>の省略形。
標準エラー出力・・・エラー情報の出力用(2 番)。2>と書く。
ファイルへのリダイレクトや別のコマンドへのパイプラインは、このうちの標準出力の方だけをつなぐ。
→ 標準エラー出力から出てきたエラーメッセージは、リダイレクト先のファイルには保存されない。
解決策
既存のファイル書き込み処理の口にリダイレクトして、そこに相乗りする。
$ ./test-status.sh -u a001 1>/tmp/log.txt 2>&1
>%1
: その後に指定した番号の出力と同じ出力先にリダイレクトする。
1 番(=標準出力)から出てくる情報が/tmp/log.txt
というファイルに書き込まれるように口がつなぎ替わって、
2 番(=標準エラー出力)から出てくる情報が 1 番出口の接続先になっているファイル書き込み用の口に流れるように口がつなぎ替わる
シェルスクリプトまとめ1
リモート操作
X を飛ばす
$ ssh -Y -C chino@192.168.1.1
-Y
: X の転送指定
-C
: 通信内容の圧縮。付けると応答性がよくなる。
SSH 越しにコマンドを 1 つだけ実行する
$ ssh chino@server /scripts/do_backup.sh
検索
$ grep -r -i -E "(香風|かふう|カフウ) ?(智乃|ちの|チノ) | Kafu Chino" /var/share/
-i
: 大文字小文字の違いを無視
-E
: 正規表現を使う
仮想端末
インストール
$ sudo apt-get install tmux
有効化
$ tmux
再接続
$ tmux attach
切断
Ctrl
+ B
→ D
※ 切断してから exit しよう
画面の生成
Ctrl
+ B
→ C
画面の切り替え(次へ)
Ctrl
+ B
→ N
画面の切り替え(前へ)
Ctrl
+ B
→ P
画面の分割(縦)
Ctrl
+ B
→ "
画面の分割(横)
Ctrl
+ B
→ %
画面の移動
Ctrl
+ B
→ ↑
, ↓
→
, ←
画面サイズの変更
Ctrl
+ B
→ Ctrl
+ ↑
, ↓
, →
, ←
画面のスクロールの有効
Ctrl
+ B
→ [
※ Q
で元の状態に戻る。
履歴
検索機能(後方検索)
Ctrl
+ R
逆方向(前方検索)にもコマンド履歴の検索をするには・・・
.bashrc
にstty stop endef
を追記する。
検索機能(前方検索)
Ctrl
+ S
端末間でコマンド履歴を共有する
.bashrc
に追記
function share_history { history -a history -c history -r } PROMPT_COMMAND='share_history' shopt -u histappend export HISTSIZE=9999
scp
ローカルのfile.txt
を 192.168.1.1 の/tmp
配下にコピーする。
$ scp ./file.txt chino@192.168.1.1:/tmp/
ログインに使ったユーザのホーム
chino@192.168.1.1:~/
他のユーザのホーム
chino@192.168.1.1:~Cococa/
chino@192.168.1.1:../../tmp/
リモートのfile.txt
を ローカルにコピーする。
$ scp chino@192.168.1.1:/tmp/file.txt ~/
ディレクトリごとコピーする
$ scp chino@192.168.1.1:/tmp/*.log /tmp/
$ scp -r chino@192.168.1.1:/tmp/results/ /tmp/
サーバからサーバにファイルをコピーする
$ scp chino@rabbithouse:/data/file chiya@amausaan:/backup/
システムの過負荷
top
システムの負荷に関する情報を、数秒間隔でリアルタイムに更新しにながら表示
load average
: 負荷の指標。CPU に処理されるのを待ってる仕事の数。1 分あたり平均で何個分の仕事が積み上がっているのかを表す値。CPU のコア数以下ならよい。
%CPU
: CPU 使用率。
TIME+
: CPU 時間。実際に CPU を使ってる時間。
過負荷の原因を探るときは「CPU 使用率が高い」、「CPU 時間も長い」プロセスがあるかどうかを見る。
C
キーを押すとコマンドの詳細表示が切り替わる。
PID
: プロセス ID
$ sudo kill プロセスID
Shift
+ P
・・・CPU 使用率でソート
Shift
+ T
・・・CPU 時間順でソート
Shift
+ M
・・・メモリー使用量順でソート
ログファイルの閲覧
$ grep "/redmine" access.log | grep -v "/live" | less
-v
: 指定の文字列を含む行を除外する
圧縮されたログファイルを grep
zcat
: 圧縮ファイルを読み込んで内容を展開しながら出力する(gzip 形式専用)
$ zcat access.log.gz | grep "/redmine" access.log | grep -v "/live" | less
zcat
: .gz, .tgz 形式
xzcat
: .xz 形式
unzip -p
: zip 形式
リアルタイム出力
$ tail -F access.log | grep "/redmine" access.log | grep -v "/live"
シェルスクリプト
シェルスクリプトを作成する。
$ vim setup.sh
実行権限を変更する。(新しく作ったシェルスクリプトはまだ実行権限が付与されていない)
$ chmod +x setup.sh
シェルスクリプトを実行する
ファイル名だけでコマンドとして実行できるのは、ファイルが/bin/
や/usr/bin/
などの特別な場所にあるときのみ
$ /home/chino/setup.sh
$ ./setup.sh
$ ~chino/setup.sh
変数
#!/bin/bash base=/var/log/nginx latest=${base}/access.log prev=${base}/access.log.1.gz cat $latest | analyze_latest.sh max zcat $prev | analyze_prev.sh max tar xvf log_result_max
tar xfv <path>
: ファイルを展開
tar cfv <path>
: ファイルを圧縮
環境変数
自分で定義しなくても$変数名
と書くと値を参照できる特殊な変数。
$HOME
: ホームディレクトリのパス
$PWD
: 現在のディレクトリのパス
$EDITOR
: 既存のテキストエディタのパス
$PAGER
: 既存のページャー(less, lv など)のパス
$USER
: 現在のユーザのユーザ名
$GROUP
: 現在のユーザののグループ名
$HOSTNAME
: そのマシンのホスト名
コマンド置換
$(コマンド列)
または`コマンド列
`
$ mv result.txt result-$(date +%Y-%m-%d).txt
必要な情報を取り出す
cut
・・・パイプで渡された内容の各行から必要な部分を切り取って返す
$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | less
"-d": 区切り文字。(一文字だけしか指定できない)
"-f": 取り出す位置
アクセスされた日付だけを取り出す。
$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d "[" -f 2 | cut -d "]" -f 1 | less
同じ内容の行を数えたい
uniq
・・・連続した重複を取り除く
sort
しておけば、すべての重複をきちんと取り除ける。
uniq -c
・・・それぞれの内容が何回登場したか、が出力される。
アクセスされたページのパスの一覧に対して、同じパスの登場回数を数える
$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | sort -r | uniq -c | cat
-r
: 降順ソート
上位と下位の項目を抽出する
上位
$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | sort -r | uniq -c | head -n 20
-n
: 出力する行数を変える
下位
$ cat /var/log/nginx/access.log | grep -v "/live" | cut -d " " -f 7 | sort -r | uniq -c | tail -n +6
-n +
: 指定行数以降を出力する。
-n -
: 指定行数以外を除いた全ての行を出力する。
CSV ファイルの並び替え
1,2,3,4,6 列目を取り出す。
$ cat test.csv | cut -d "," -f 1-3,4,6 | sort -t "," -k 3 -n | cat
-t
: 区切り文字の指定
-k
: 指定した列の内容で並び替え
-n
: 数の部分を数値として解釈して数の大小で並べ替える
※ cut と sort でオプションの名前が違う。
結果を上書き出力
$ cat test.csv | cut -d "," -f 1-3,4,6 | sort -t "," -k 3 -n > sorted.csv
>>
: 追記
オプション引数
#!/bin/bash source=$1 target=$2
$ ./script.sh first second third
first・・・$1
second・・・$2
third・・・$3
名前付き引数
#!/bin/bash while getopts b: OPT do case $OPT in b) base="$OPTARG" ;; n) next="$OPTARG" ;; p) previous="$OPTARG" ;; o) output="$OPTARG" ;; esac done
$ ./opt_script.sh -b /var/log/nginx/access.log2.gz -n /var/log/nginx/access.log.1.gz
処理分岐
$ script.sh foo bar
if [ $# = 2 ] # 引数の個数が2に等しい then echo "Hello!" else echo "Good afteroon" fi
target=$1 if [ "$target" != "" ] # 引数が渡された then echo "Hello!" else echo "Good afteroon" fi
繰り返し
#!/bin/bash for filename in rabbithouse.log fleur_du_lapin.log amausaan.log do ./create-report.sh $filename done
#!/bin/bash for filename in `cd /var/log/nginx; ls *.log | grep -v error.log` do ./create-report.sh $filename done
シェル関数
#!/bin/bash # 主要な処理をmain関数として先に定義しておいて最後にそれを呼び出すのが定石 main() { report rabbithouse.log rabbithouse-${today}.csv /shared/rabbithouse/reports report fleur_du_lapin.log ${today}.csv /shared/fleur_du_lapin/reports report amausaan.log ${today}.txt /shared/amausaan/reports } today() { date +%Y-%m-%d } report() { source=$1 report=$2 outdir=$3 ./analyze_mail_log.sh $source $report mkdir -p $outdir mv /tmp/${report} ${outdir}/ echo "$source を処理しました" } main
exit 1
: スクリプトの実行を中断して終了ステータスを 1 にする
return 1
: 関数の実行を中断して終了ステータスを 1 にする
※ 関数のなかで$1
と書くと、シェルスクリプトの引数ではなく、関数の引数を参照する。ので、関数内で引数を参照するときは関数外でいったん別の名前の変数として定義しておく。