docker volumeの名前がハッシュ値のものを一括削除する
普段開発環境はdockerを使っています。dockerを使って開発していくとvolume名が64桁のハッシュ値のものがどんどん増えていきディスクを圧迫していきます。ディスクの容量がいっぱいになると、dockerの起動がしなくなったりして開発に支障が出たことがありました。それからはこまめに削除していますが、volume名がハッシュ値のものを一括で削除するスために下記スクリプトで削除しています。
$ docker volume ls | awk 'length($2)==64 {print $2}' | xargs docker volume rm
下記にそれぞれのコマンドの内容を簡単に記載しておきます。
docker volume ls
docker volume ls
により全てのvolumeを表示する。
1列目はdriver
が表示され、2列目がvolume name
が表示される。
$ docker volume ls DRIVER VOLUME NAME local 0b54333e1df6786dec2c213dae87d10ccc75a85ceade2498cc6abbed140c5ab9 local 2f87d4b910d4dae508c04edb7d004a7aecc1eddc7dc0e60d92d29306f7346279 local 3ee8993cda4164ab5e764ea7a097bd67cb0e4bc3b3702073a957c58c4c64dc59 local pgsql-data
awk
awk
コマンドにより上記のdocker volume ls
で出力された結果を加工する。
awk
コマンドは空白やカンマなどで区切られたテキストを処理できるコマンドである。文字列を検索したり、文字列を抜き出したりすることが可能である。
今回の場合、文字列を検索しているはlength($2)==64
の箇所である。これは2列目の文字の長さが64文字のものを検索している。
そして{print $2}
で2列目を抜き出している
$ docker volume ls | awk 'length($2)==64' DRIVER VOLUME NAME local 0b54333e1df6786dec2c213dae87d10ccc75a85ceade2498cc6abbed140c5ab9 local 2f87d4b910d4dae508c04edb7d004a7aecc1eddc7dc0e60d92d29306f7346279 local 3ee8993cda4164ab5e764ea7a097bd67cb0e4bc3b3702073a957c58c4c64dc59 $ docker volume ls | awk '{print $2}' 0b54333e1df6786dec2c213dae87d10ccc75a85ceade2498cc6abbed140c5ab9 2f87d4b910d4dae508c04edb7d004a7aecc1eddc7dc0e60d92d29306f7346279 3ee8993cda4164ab5e764ea7a097bd67cb0e4bc3b3702073a957c58c4c64dc59 pgsql-data $ docker volume ls | awk 'length($2)==64 {print $2}' 0b54333e1df6786dec2c213dae87d10ccc75a85ceade2498cc6abbed140c5ab9 2f87d4b910d4dae508c04edb7d004a7aecc1eddc7dc0e60d92d29306f7346279 3ee8993cda4164ab5e764ea7a097bd67cb0e4bc3b3702073a957c58c4c64dc59
xargs
標準入力からコマンドラインを構築して実行することができる。例えばcommandA | xargs commandB
でcoomandA
で実行した結果をcommandB
の引数としてcommandB
を実行する。
今回のスクリプトでは| xargs docker volume rm
としているのでawkコマンドで得られた64桁のvolume名をdocker volume rm
に引数として渡している。docker volume rm volume名
でvolume名
のvolumeを削除する。
Rustのエラーハンドリングの基本
普段仕事ではRubyを使ってWebアプリケーションの開発をしているのですが、Rustに興味があり、勉強をしています。
Rustのエラーハンドリングについて勉強したのでまとめてみました。
Rustのエラーハンドリング
多くのプログラミング言語にはエラーハンドリングに例外や戻り値を利用しますが、Rustでは戻り値を使ってエラーハンドリングをします。
Rustのエラーハンドリングは大きく分けて2つの種類があり、リカバリーできるエラーとリカバリーできないエラーであります。
リカバリーできるエラーはResult<T, E>
値があり、リカバリーできないエラーはpanic!
マクロなどがあります。
リカバリーできないエラーはプログラムの実行を中止するが、多くのエラーはプログラムを完全にストップさせるほどの必要はないため、基本的にはResult
型を使ってエラーハンドリングします。
Result型
Rustには例外がないが、代わりに失敗する可能性のある関数はResult
型を返してエラーの場合を対処します。
Result
型は下記のようなenumとしてOk
とErr
の2列からなるよう定義されています。
enum Rusult<T, E> { Ok(T), Err(E)
Ok(T)
は成功結果でErr(E)
はエラー結果を返します。
T
とE
はジェネリックな型引数を表しており、T
が成功したときに返される値の型を表し、E
が失敗したときに返される型を表します。
Result型の処理方法
Result型の処理方法の一つはmatch式を使い、関数がResult
型を返したときにパターンマッチさせます。
use std::num::ParseIntError; fn parse_str(num_str: &str) -> Result<u32, ParseIntError> { num_str.parse::<u32>() } fn main() { let num_str = "3"; match parse_str(num_str) { Ok(num) => println!("{}", num), Err(err) => eprintln!("{}", err), } let num_str = "str"; match parse_str(num_str) { Ok(num) => println!("{}", num), Err(err) => eprintln!("{}", err), } }
上記を実行すると下記のように出力されます。
3 invalid digit found in string
parse_str
へ引数に渡された文字列により、その処理の結果に対してのResult型が返されます。その返された結果をmatch
式でパターンマッチさせ、返された結果がOk
かErr
によりそれぞれの結果を処理します。
また、match
式を使う以外の別の方法としてResult
型に用意されているメソッドがあるので、こちらを使って処理することができます。
use std::num::ParseIntError; fn parse_str(num_str: &str) -> Result<u32, ParseIntError> { num_str.parse::<u32>() } fn main() { let num_str = "3"; parse_str(num_str) .map(|num| println!("{}", num)) .unwrap_or_else(|err| eprintln!("{}", err)); let num_str = "str"; parse_str(num_str) .map(|num| println!("{}", num)) .unwrap_or_else(|err| eprintln!("{}", err)); }
実行結果はmatch
式を使ったコードと同じ結果となります。
map
はResult型の値がOk
時に、map
の引数に与えられた関数かクロージャをOk
の中の値に対して実行しその結果をOk
の中身として返し、Err
の場合はそのまま値を返します。
unwrap_or_else
はOk
の場合はそのまま値を返し、Err
のときは関数かクロージャを実行してその結果を返します。
?演算子
エラーハンドリングするためにすべてのエラーをその場で処理してmatch
式を記載する必要はない。Rustでは?
演算子を用いてエラーハンドリングを呼び出し元に任せることができます。
例えば、下記のコードのようなコードの場合、parse_two_str
メソッド内でparse
メソッドを2回呼び出しているが、それぞれのエラーハンドリングはparse_two_st
の呼び出し元に任せることができます。
use std::num::ParseIntError; fn parse_two_str(first_num_str: &str, second_num_str: &str) -> Result<(u32, u32), ParseIntError> { let first_num = first_num_str.parse::<u32>()?; let second_num = second_num_str.parse::<u32>()?; Ok((first_num, second_num)) } fn main() { let first_num_str = "3"; let second_num_str = "5"; match parse_two_str(first_num_str, second_num_str) { Ok((first_num, second_num)) => println!("{}, {}", first_num, second_num), Err(err) => eprintln!("{}", err), } let first_num_str = "str"; let second_num_str = "5"; match parse_two_str(first_num_str, second_num_str) { Ok((first_num, second_num)) => println!("{}, {}", first_num, second_num), Err(err) => eprintln!("{}", err), } }
実行結果は下記のような結果になります。
3, 5 invalid digit found in string
関数parse_two_str
は与えられた引数それぞれに対してparse
メソッドを実行しており、Result型が返されます。?
演算子を使用することでOk
が返された場合にはResult
型を解いてOk
列挙列の中身を取り出し、プログラムを継続する。Err
が返された場合は呼び出し元にErr
値を即時にリターンします。
?
演算子と同じような動作をmatch
式と使って実装できます。
fn parse_two_str(first_num_str: &str, second_num_str: &str) -> Result<(u32, u32), ParseIntError> { let first_num = match first_num_str.parse::<u32>() { Ok(first_num) => first_num, Err(err) => return Err(err), }; let second_num = match second_num_str.parse::<u32>() { Ok(second_num) => second_num, Err(err) => return Err(err), }; Ok((first_num, second_num)) }
異なる種類のエラーの扱い
上記のparse_str
を修正して標準入力から入力した文字をparse
するメソッドに変更したいとします。
その場合、標準入力を読み取るためにstdin
を使用するが、ただ追加しただけだと下記のようになります。
use std::num::ParseIntError; use std::io; fn parse_str() -> Result<u32, ParseIntError> { let mut buffer = String::new(); io::stdin().read_line(&mut buffer)?; let num = buffer.trim().parse::<u32>()?; Ok(num) } fn main() { match parse_str() { Ok(first_num) => println!("{}", first_num), Err(err) => eprintln!("{}", err), } }
このコードはコンパイルに失敗します。なぜならio::stdin().read_line(&mut buffer)
のErr
の型がParseIntError
と違うからです。read_line
のErr
の型はstd::io::Error
です。
?
演算子の作用でErr
型を定義された関数の戻り値(今回の場合、ParseIntError
)のErr
型に変換しようとするが、ParseIntError
型への変換は定義されていないため、コンパイルエラーとなります。
これを解決する方法は、関数のErr
型の戻り値の定義をトレイドオブジェクトで定義すると解決します。定義はBox<dyn std::error::Error>
と書きます。標準ライブラリーのエラーはすべてError
トレイトを実装しているため、トレイトオブジェクトで定義すると変換できます。
修正したコードは下記になります。これでコンパイルができるようになります。
use std::io; fn parse_str() -> Result<u32, Box<dyn std::error::Error>> { let mut buffer = String::new(); io::stdin().read_line(&mut buffer)?; let num = buffer.trim().parse::<u32>()?; Ok(num) } fn main() { match parse_str() { Ok(first_num) => println!("{}", first_num), Err(err) => eprintln!("{}", err), } }
まとめ
Rustのエラーハンドリングについて基本的な部分について勉強したことをまとめてみました。
実際にエラーハンドリングはfailure
というライブラリが使われるのが一般的みたいです。こちらについてもいずれまとめてみたいと思ってます。
参考文献
- The Rust Programming Language
- プログラミング言語Rust
- プログラミングRust Jim Blandy (著), Jason Orendorff (著), 中田 秀基 (翻訳)
Gitのコマンド集
普段仕事や日々の勉強でソースコードの管理としてGitを使っています。 そこで、普段良く使うコマンドやトラブルになったときに解決できたコマンドなどをまとめてみました。 このブログを読んでくださっている皆様にも役立つと思います。
コミット内容を確認する
指定したコミットの内容を確認します。また、確認したいコミットのハッシュ値とファイルを指定することで、そのファイルのコミット時の変更内容が確認できます。
# HEADの指すのコミット内容 $ git show # ファイル、コミットを指定 $ git show [コミットのハッシュ値]:[ファイル名]
コミットメッセージのみを変更する
間違えた内容のコミットメッセージを書いてしまったときに、コミットメッセージのみを修正します。コミット時に起動するエディタが立ち上がりません。
$ git commit --amend --only -m '[コミットメッセージ]'
前回のコミットからコミット内容を修正する
前回のコミットからファイルを修正します。
$ git add [変更したファイル] $ git commit --amend # --no-editでコミットメッセージはそのままで修正内容だけを書き換える $ git commit --amend --no-edit
直前のコミットを取り消す
直前のコミットを取り消します。引数によりコミットを取り消したあとの変更内容の扱いが変わります。
--hard
をつけなければ変更した内容はそのまま残る
# コミットを取り消して、変更内容を残す $ git reset HEAD^ # コミットを取り消して、変更内容は残したまま、ステージングに追加された状態にする $ git reset HEAD^ --soft # コミットを取り消して、変更内容も取り消す $ git reset HEAD^ --hard
追跡されていないファイルを削除する
# untrakedなファイルやディレクトリを確認する $ git clean -dn # untrakedなファイルやディレクトリを削除する $ git clean -df
直前のコミットからファイルを削除する
直前のコミットからファイルの変更内容を修正、または直前のコミットから新しいファイルを追加したときにファイルを削除します。
$ git checkout HEAD^ [ファイル名] # ファイルを修正したい場合は修正してステージングに追加する $ git add [ファイル名] $ git commit --amend --no-edit
gitだけから削除し、指定したファイルをuntraked
な状態にしたい場合
$ git rm --cached [ファイル名] $ git commit --amend --no-edit
直前のコミット以外のコミット内容を取り消す
直前のコミット以前にコミットした内容を書き換えます。 例えば最後に行ったコミットから4番目を変更したい場合。
$ git rebase -i HEAD~4
するとエディタが立ち上がるので下記のように変更します。
pick 8f74ea3 first commit pick a358c18 second commit pick 1e6431f third commit pick 8c8509c fourth commit
edit 8f74ea3 first commit pick a358c18 second commit pick 1e6431f third commit pick 8c8509c fourth commit
変更後エディタを閉じるとrebaseが開始され、先程指定したコミットがHEADを指した状態で停止します。
ファイルを変更し、ステージングに追加してから下記コマンドを実行します。
$ git commit --amend --no
これによりエディタで指定されたコミットは修正されました。そして残りのコミットに対してrebaseを進めます。
$ git rebase --continue
コンフリクトが起こればその都度上記のように修正して解消していきます。
修正した変更をもとに戻す
HEADのコミットから変更した内容をHEAD時の状態に戻します。これはステージングに追加したファイルの変更をすべて戻します。
$ git reset HEAD --hard # リモートブランチと同じ状態に戻すにはHEADではなく、リモートブランチを指定する $ git reset [origin/ブランチ名] --hard
ステージングに追加した内容を除いた変更のみ、元に戻す場合は下記コマンドで変更します。
$ git checkout . # ファイルを指定してもどに戻すこともできる $ git checkout [ファイル名]
ステージングに追加した内容を取り消す
$ git reset # ファイルを指定してもどに戻すこともできる $ git reset -- [ファイル名]
ステージングに追加した内容と追加してない内容を入れ替える
ステージングに修正内容を追加してから、追加で修正していき内容を入れ替えたくなった時に使用します。
# -kでステージングにある修正を除く $ git stash -k $ git reset --hard $ git stash pop $ git add -A
ブランチを削除する
マージ済みなどの必要のないブランチを削除します。
$ git branch -d [ブランチ名] # マージされていないブランチを削除する $ git branch -D [ブランチ名]
また、複数ブランチを削除したいときは正規表現などで指定して削除します。
例えばmasterブランチ以外を削除するには下記コマンドを入力します。
$ git branch | grep -v master | xargs git branch -d
gitの操作を取り消す
gitはgitのコマンドなどの操作したログを保存しているので、その履歴からコマンドの取り消しができます。
$ git reflog
過去のgitのコマンド履歴が表示されるので戻りたいハッシュ値を探してresetします。
$ git reset --hard [ハッシュ値]
新しいブランチを作る
現在のブランチからの分岐で新しいブランチを作ります。
$ git checkout -b [新ブランチ名] # コミットのハッシュ値を指定して、新しいブランチに修正内容を移動します。 $ git checkout -b [新ブランチ名] [コミットのハッシュ値]
別のブランチのファイルの修正内容を反映させる
別のブランチのファイルの修正内容をの内容を現在のブランチに反映させます。
$ git checkout [ブランチ名] -- [ファイル名]
別のブランチのコミット内容を反映させる
別のブランチで変更した内容を別のブランチでもその内容を反映させたいときにおこないます。
# あらかじめ反映させたいコミットのハッシュ値を確認しておく $ git cherry-pick [コミットのハッシュ値]
リモートのブランチの内容をローカルブランチに反映させる
他の人と同じtopicブランチで作業をしていて、他の人がforce pushなどして自分のローカルにpullできなくなったとき、下記コマンドでローカルブランチを上書きします。
$ git fetch origin $ git reset --hard oritin/[ブランチ名]
特定バージョンにファイルを戻したい
特定のコミットのときのファイル内容に戻したとき、コミットのハッシュ値を確認しておき、下記コマンドで反映させます。
$ git checkout [コミットのハッシュ値] -- [ディレクトリ/ ファイル名]
ブランチ名を変更する
ブランチ名を変更した時は下記コマンドを入力します。
$ git branch -m [ブランチ名] [新ブランチ名]
修正した内容を一時的に別の場所に退避させる
変更した内容を一時的に退避させて、他のブランチに切り替えたり、他の変更作業を行ったりします。
# -uでuntracked fileも退避の対象にする $ git stash -u # 作業ディレクトリから特定のファイルのみstashしたい時 $ git stash [ファイル名] # branchを切り替えたり、作業したあと $ git stash apply
stashしたファイルを現在のブランチに適用します。
# stashしたlist一覽を表示 $ git stash list # 適用したいstashを選び、ブランチ適用する $ git stash apply "stash@{n}" # 適用したあとのstashを適用と同時に削除したいとき $ git stash pop "stash@{n}"
複数のコミットを一つにまとめたい
topicブランチで作業しているときに細かくコミットしながら作業をすすめると最後にtopicブランチの内容をすべてまとめていたいことがあります。
このときは下記コマンドでコミットをまとめることができます。
$ git rebase -i HEAD~4
edit画面が下記のように表示される
pick 8f74ea3 first commit pick a358c18 second commit pick 1e6431f third commit pick 8c8509c fourth commit
例えば、上記のsecond commit
から新しいコミットをまとめたい時、second commitにすべてまとめたいコミットを組み合わせたいとすると、second commit以外のpick
の箇所をf
に変更し保存して閉じると一番古いコミットにすべてまとめることができます。
pick 8f74ea3 first commit pick a358c18 second commit f 1e6431f third commit f 8c8509c fourth commit
組み合わせるコミットの名前を変更したい場合はpick
をf
の代わりにs
に変更するとエディタが立ち上がりコミットメッセージを編集することができます。
過去のコミットを見る
$ git log # 各コミットで変更された内容をみたいとき $ git log -p # 変更されたファイル一覽などをみたいとき $ git log -stat # ある時間内のlogを見る。たとえば10/1~11/7のlogを見たい時 $ git log --since='2019-10-1' --until='2019-11-7' # ある文字列が追加されたコミットのみを抜き出す $ git log -S [検索したい文字列] # 特定のファイルを含むコミットを検索する $ git log -- [ディレクトリ/ ファイル名] # マージコミットのみ表示する $ git log --merges # マージコミットを覗いて表示する $ git log --no-merges # マージコミットを覗いて表示する $ git log --no-merges # コミットログを検索する $ git log --grep="[検索したい文字列]"
ステージングに追加したファイルの変更を確認します。
$ git diff --cached
コミットやブランチ間の変更内容を確認したい
HEADのコミットやブランチと以前のコミットや別のブランチの内容を比較します 。 HEAD以外のコミットやブランチも同士もdiffを取ることは可能です。
# HEADのコミット内容と以前のコミット内容の差分を確認する $ git diff HEAD [コミットのハッシュ値] # 上記のファイルのみの差分を確認する $ git diff HEAD:[ファイル名] [コミットのハッシュ値]:[ファイル名] # ブランチ間の差分を確認する $ git diff master:[ファイル名] develop:[ファイル名]
あるコミットが含まれているマージコミットを探す
あるコミットを含んだマージコミットを検索します。
# コミットのハッシュ値と検索するブランチを指定する $ git log [コミットのハッシュ値]..[ブランチ名] --ancestry-path --merges
各行が変更されたコミットを確認する
対象のファイルで各業が変更されたコミットを調べたいときに下記コマンドで確認します。
$ git blame [ファイル名] # -Lで行番号を指定できる $ git blame -L n,m [ファイル名] # 関数名でもしてできる $ git blame -L :funciton [ファイル名]
変更の内容を同時に表示したい場合はlog
コマンドで表示させることができます。
$ git log -L n,m:[ファイル名]
コミットを取り消す
特定のコミットを取り消します。revert
で取り消すとコミットとして記録されます。
$ git revert [コミットのハッシュ値] # コミットメッセージを編集する $ git revert [コミットのハッシュ値] -e # コミットせずステージングに追加した状態の場合 $ git revert [コミットのハッシュ値] -n # マージコミットを取り消すとき、2つの親にどちらを戻すか指定するひつがある # git log で Merge: xxxxxxx yyyyyyy のコミットIDの一番目か2番目を指定する $ git revert -m 1 [マージコミットのハッシュ値]
取り消した内容をコミットに残したくない場合はrebase
でコミットを取り消します。
# 取り消したいコミット値よりも前のコミット値を指定 $ git rebase -i [コミットのハッシュ値]
エディタが立上がり、下記のように表示されます。
pick 8f74ea3 first commit pick a358c18 second commit pick 1e6431f third commit pick 8c8509c fourth commit
上記からいらないコミットを削除し保存して、エディタを終了すると削除したコミットが削除されます。
リベースやマージを取り消す
リベースやマージの変更のリセットする場合はこの変数を指定することで取り消すことができます。
$ git reset --hard ORIG_HEAD
以上で、今までよく使っているまたは、使ったことのあるGitのコマンドをまとめてみました。 また新しくコマンドを使用したときに随時更新してきたいと思っております。
ブログ開設してみました
はじめまして。hideokaです。ブログを開設して技術的な内容や普段の出来事などを発信してみたいと思いつつ、今までできてなかったのでブログを始めてみました。
プログラマになってもうすぐ2年になります。仕事ではサーバーサイドエンジニアとしてWebの自社会社でプログラマとして働いています。社会人歴は8年近いですが、もともとは別の分野で働いていました。ジョブチェンジした形になります。きっかけはプログラマになって最先端の技術に携わりたいと思い、思い切って仕事を変えてみました。この辺の生い立ちもまたの機会にお話できたらと思ってます。
記事の内容は仕事で使っているRuby, Vue.js, shell script, vimなどや今一番興味があるRustなどの技術内容をメモしていきたいと考えてます。もちろんブログとして公開しているので正確な情報でわかりやすいようにまとめていきたいです。
それでは、次回から技術記事をどんどん書いていきます。