それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

date コマンドでミリ秒→日付、int 秒 → 日付へそれぞれ数値を日付表記にフォーマットする

int 秒を日付にするには

引数 -d に @11234567 をつけます。

takuya@~$ date -d @1566808184 +"%F %T"
2019-08-26 17:29:44

ミリ秒の場合。

date コマンドはミリ秒を解釈しないので、いったんint へ数値計算してやる

takuya@~$ date -d  @$((1566808184122/1000)) +"%F %T"
2019-08-26 17:29:44

$(()) で四則演算

数値計算の方法は、過去記事にあるので、そっちへ

bashの似てて紛らわしいもの (( / $(( - それマグで!

date コマンドでフォーマット

date コマンドでフォーマットの方法は、過去記事があるので。参照

dateコマンドでiso/rfc日付時刻タイムゾーンをぱぱっとフォーマットして作る - それマグで!

逆に int 秒がほしいとき

dateコマンドで unix epoch time の int 秒を取得する - それマグで!

npm で作られたプロジェクトの依存モジュールを更新する npm-check-updates

npm で作ったパッケージのnode_module の更新をしたい

npm でインストールしたパッケージはどうも使い捨ての傾向があり、アップデートを継続するにはちょっと手間が必要で。

更新可能なパッケージをを見つけるには

npm のサブコマンドを使えばできる

npm update 
npm outdated

これらのコマンドを実行して更新可能な最新版にアップデート可能なモジュールを見つけて更新していくわけだが。

めんどくさい。

npm-check-updates

npm-check-updates 略して、 ncu というパッケージがある。

これを使うと package.json の中身を更新してくれるわけで、便利そうである。

アップデート手順

npm install npm-check-updates
npx ncu -u 
npm update 
npm install 

これで、一通りのアップデートが可能になる。思ってたより楽である。

開発者が想定したpackage.json を更新するのである。当然であるが、依存モジュールをアップデートすると、decrepcated なメソッドやバージョン差異による動作不良に悩まされることもあるので注意が必要である。

参考資料

which コマンドでPATHにある同名のコマンドを全部列挙する

PATH の優先順位に悩まされるたときに which

which コマンドを使うと 環境変数 PATH にあるコマンドのうち、最初に見つかったもの(実行されるもの)を表示してくれます。

which python 

どのコマンドが実行されるのか調べるのに便利ですし、よく使ってると思います。

同名のコマンドで上書きされてないか?

pyenv や nvm や rbenv などを使っていると、あれ?バージョンが合わない?パスがおかしい????

などという事がよくあります。

こういうときにPATHに含まれる全てのもの列挙できたらと思いませんか

すべてを表示する which -a / --all

コマンドがいくつも同じ名前で入っている場合に、全てを表示してくれます。

表示順は、PATHの優先順位が高い順に表示してくれます。

takuya@-api$ which -a grep
/Users/takuya/.bin/grep
/usr/bin/grep
takuya@-api$ which -a java
/usr/bin/java
takuya@-api$ which -a python
/Users/takuya/.pyenv/shims/python
/usr/local/bin/python
/usr/bin/python

これをすることで、 python が同名でいくつもあることがわかったり、$PATHに同じパスが重複していたり、bashrc や pipenv shell などで .bashrc の意図した動作にならない。 順序がおかしくなっていることに気づくことができます。

関連資料

https://takuya-1st.hatenablog.jp/entry/2017/04/06/153337

参考資料

  • which -h

systemd のdaemon のサービスを定期的に再起動する unit ファイルの書き方-timerを省略する

systemd のサービスを定期的に再起動する

systemd で作ったサービスを定期的に再起動したい。

定期的に再起動する必要があるのか。と問われれば、私自身も答えに窮するのだけれど。

今回は pip install しているpython パッケージや npm run で起動している元になっている npm/node_moduleを、定期的更新して再起動をしたいなと思ったんですよ。

systemd の書き方

[Service]
Restart=always
RuntimeMaxSec=864000

unit ファイルに上記のように書くと定期的に再起動がかかる。

仕組みの説明

RuntimeMaxSec による実行時間の制限

RuntimeMaxSec=60

RuntimeMaxSec をつけることで、サービスのプロセスの継続実行時間を秒単位で指定できる。今回は 60*60*24*10 = 864,000 を指定した。

Restart による再起動の設定

Restart=always

Restart の設定を alwaysにしておくと、サービスに定義したプロセスが異常終了など、終了したときに自動再実行によりプロセスが起動している状態を維持してくれる。

組合わたら、定期的に再起動

これらを組合わたら、定期的に再起動のコマンドが実現する

[Service]
Restart=always
RuntimeMaxSec=864000

組み合わせてしまうと、指定時間経過後にプロセスが終了して、always により再度サーバーが実行される。

検討したその他の手法との比較など

crontab に書いてしまうと、管理がsystemd と分かれてしまうのであまり好ましくないし。

サーバーが自分を自分で再起動するように sleep を使った定期的に再起動が仕込まれた起動用python スクリプトを書くのは煩雑すぎるし。

systemd の timer を使っても、設定が2つに別れてしまって面倒だ。

今回採用した方法だと、設定ファイルが1つにまとまるので書き捨てて仕様書を残さなくても気付けるメリットが有る。

systemd の設計者は、こういう利用は想定外なんだろうと思うんだけど、1箇所に必要な設定が集まるのは嬉しい

pip や npm の更新が辛い

npm でパッケージをいれて、npm run で動かせるサーバーアプリケーションがおおいけど、パッケージはlock されて基本的には更新を考慮してない。

debian の apt などは セキュリティ・パッチが含まれた deb などをaptの設定自動的にダウンロードして再実行してくれる。ほんとうにこの差は大きいと思う。

gitlab とは自動的に自分で自分を更新してくれるし、そういう意味で node のnpm で作られたサーバーって今後危ういなぁなどと思う。

service と timer を分けて書くとき

再起動するサービスと、タイマー、この2つのユニットを分けて書くときは、ネットにいっぱいサンプルが転がってる。

うちのサイトでの作成サンプルは、この記事でtimer/serviceのunits を使ってsystemdのユニットを定期的に起動している。

分ける場合のサンプル → https://takuya-1st.hatenablog.jp/entry/2020/04/09/051239

分ける場合のtimer の書式 → https://takuya-1st.hatenablog.jp/entry/2020/04/24/032822

再起動スクリプト+Service+Timer+タイマ書式の4つが必要になる。こういうことを考えるのが面倒なので再起動がすぐ終わるServiceなら今回紹介しているMaxTimeによる再起動が楽だと思うの。

関連資料

参考資料

bash で三項演算子っぽく処理を書くには

bash でも三項演算子を使いたい

かんたんな、初期値nullチェックとかで三項演算子的なことをよくやります。

bash でもやりたいなと思って調べててみたけど、なかった(あったので追記書きます。)。

条件分岐を使って原始的な手法がsh/bash で使えるのでご紹介。

三項演算子の変わりの原始的手法

[[  $var == abc ]] && echo OK || echo NG

サンプル

#!/bin/bash

## 変数
a=1

echo TRUE になる場合
[[ $a == 1 ]] && echo TRUE || echo FALSE

echo FALSE が実行される場合
[[ $a == 0 ]] && echo TRUE || echo FALSE

三項演算子bash に存在しない

将来のアップデートでbash には搭載されるかもしれないけど、sh には永遠に搭載されないだろし

条件判定を組み合わせて三項演算子っぽく記述できる方法は便利だし採用していきたい。

三項演算子の書き方(追記

三項演算子はsh にはなくbash 独自の機能 (( ))を使えば、書くことが出来ました。

a=1
b=$(( a !=0 ? -1 : 0  ))

ちなみに、(( ))のArithmetic expressions を書くときは、変数に$を付けない。

実際に動かしてみた例はこちら。

takuya@~$ unset a b
takuya@~$ a=1
takuya@~$ b=$(( a !=0 ? -1 : 0  ))
takuya@~$ echo $b
-1
takuya@~$
takuya@~$ unset a b
takuya@~$ a=0
takuya@~$ b=$(( a !=0 ? -1 : 0  ))
takuya@~$ echo $b
0

2019-08-29 訂正

三項演算子ありました。 (( )) を使えば、C言語スタイルの演算ができるので、もしやと思って調べたら・・・

参考資料

https://wiki.bash-hackers.org/syntax/arith_expr

systemdのジョブの起動前、起動後に処理を記述する preExec / PostExec

systemd で自動起動するファイルを記述したが pip のアップデートで苦労する

systemd でdaemon を起動しているのですが、pip 関連でアップデートが頻繁にあるモジュールだと、前もって更新してあげないとエラーになることがる。

systemd で事前処理・事後処理を書けないか調べてみたらあった

ExecStartPre を使うと、起動前に実行してくれるようだ。

ExecStartPre=, ExecStartPost= Additional commands that are executed before or after the command in ExecStart=, respectively. Syntax is the same as for ExecStart=, except that multiple command lines are allowed and the commands are executed one after the other, serially.

複数行の処理は?

ExecStartPreを複数書けばいいらしい

書いてみた。

pipenv に入れた pip や requirements.txt をDaemonの起動前に、更新をするように書きました。

[Unit]
Description=Splattoon splatnet2ink

[Service]
WorkingDirectory=/PATH/TO/project
ExecStartPre=/PATH/bin/python /PATH/TO/pipenv/bin/pip install --upgrade pip 
ExecStartPre=/PATH/TO/pipenv/bin/python /PATH/TO/pipenv/bin/pip install --upgrade -r  requirements.txt
ExecStart=/PATH/TO/pipenv//bin/python /PATH/TO/pipenv/daemon.py -M

[Install]
WantedBy=default.target

これで更新処理を別途書く必要がなくなる。

これまでは、ライブラリ更新処理を、別途 timer にしてたり、更新後再起動をタイマーにしてたのですが、不便でした。

これで、PCを起動時、ユーザー systemd 空間なら、ログイン時に更新されるはず。だから timer 関連のsystemd ファイルが減って管理が楽になりそうです。

参考資料

https://www.freedesktop.org/software/systemd/man/systemd.service.html

ついに牙を向いたPublic DNS

Cloudflare の 1.1.1.1 が dns 応答を拒否し始める。

巨大になった組織の宿命というか、世間のレベルより「踏み込んだ」対応をして自粛をしないとダメなんだろうけど、管理は、インターネットな文化と相容れないと思ってた私の脳みそは時代遅れで腐っているのでしょうかと。

www.itmedia.co.jp

DoH も危ない

DNS over HTTPSFirefox のも Cloudflare が請負っているので、あちらも同じことになると思う。

自前で dns が必要

public dns が使えないなら、昔のように、自前で public dns を建てるしか無い。

unbound をインストールするしか。

しかし、自宅ならともかく、4G/5Gのモバイルネットワークでどうするんだよこれ。 DNSを支配される悪夢だ・・・

ユーザー毎の systemd を使ってシステム全体設定と個人用設定を分ける。

systemd のユーザーごとの設定というのがあります。

systemd は sudo 権限がないと動かないと思ってませんか。 タイマーや自動起動インスタンスやサービスはもう、ユーザー空間でできるのです。

ユーザーモードでの systemd というのがあります。

引数 --user でユーザーモード

systemd 関連のコマンドに引数の ---user をつけると、ユーザー空間での、ユーザーに固有の systemd 空間を扱えます。

自分自身でログインしているときに、自分のsystemd 関連サービスのステータスを表示するには次のようにします。

takuya@:~$ systemctl  --user status 

f:id:takuya_1st:20190809001329p:plain

systemd サービスの作成

サービスの作成は簡単です。通常の systemd となんら違いはありませんでした。

systemd のサービスは、 ~/.config/systemd/user/ に設置する。記述方法は、通常のsystemd となんらかわりがありません。

簡単に言えば、作るファイルの場所がいつもと異なるだけ、root権限がいらない。自分権限で完結している。

ユーザー毎のsystemd のサービスを作ってみる。

それでは、試しに、自分だけのサービスを作ってみたいと思います。

最初に、ディレクトリを準備します。

mkdir -p ~/.config/systemd/user/

つぎに、service ファイルを作成したいと思います。

touch  ~/.config/systemd/user/my-loop.service

ファイルの中身を記述します。

takuya@:~$ cat   - >  ~/.config/systemd/user/my-loop.service  
[Unit]
Description=サービスを作成する練習。

[Service]
ExecStart=bash -c 'while : ; do echo 1 ; sleep 1 ;done '

[Install]
WantedBy=default.target

有効化します。

takuya@:~$ systemctl  --user enable my-loop.service 
Created symlink /home/takuya/.config/systemd/user/default.target.wants/my-loop.service → /home/takuya/.config/systemd/user/my-loop.service.

起動します。

起動方法は、通常作業で sudo 作業と同じです。

f:id:takuya_1st:20190809002950p:plain

ね?簡単でしょ。もう sudo 権限なんてもらわなくても簡単に自分でDaemon起動ができるんですよ。

LXDやLXCがり、snap があり、ユーザー空間を切り離している今の時代のLinuxには、非常に心地よいsystemd ではないでしょうか。

いつ実行されるのか

ユーザーがログインしたときです。最初のログインで実行されます。複数ログインしてログインする際ではなく、システムが起動してから最初のログインです。

ログアウトしたら終了される?

されません。ログアウトしても動作してます。 user のログイン session とは切り離されているので。環境変数も渡されないっっていうのと同じでしょうね。

起動時にならないの?

起動時に、指定ユーザーの systemd を全部ロードするには

sudo loginctl enable-linger username

これで、起動時に指定したユーザーのsysd が実行できますが、起動時に実行するのであれば、もうsudo で /etc/systemd に書くよね。

使いようによっては便利だと思います。

ubuntu デスクトップなどを利用しているときのログイン時に実行などするものを、ある程度こちらに持ってこれます。

参考資料

systemd/ユーザー - ArchWiki

ubuntu でもCtrl-Shift-ESC でタスクマネージャーを起動したい

Ctrl −Shift - Esc でタスクマネージャーを起動したい

強制終了をしたいときとか pkill で構わないんだけど、ついつい、癖で押しちゃうので、ショートカットにする

ショートカットの設定画面

f:id:takuya_1st:20190807021807p:plain

コマンドを指定する。

Gnome Desktop でWindowsのタスクマネージャーに相当するのは、デスクトップアプリの gnome-system-monitor が相当するので、それを利用することに。

f:id:takuya_1st:20190807021740p:plain

忘れそうなのでメモ

自分で設定したことを忘れそうなので、メモを取る

任意のフォルダを隠しフォルダにして非表示にする ~.hidden を使って snap フォルダを隠す

Gnomeで指定したファイルを不可視にしたい。

通常であれば、ファイル名の先頭にドットをつけt,えdotfiles にして非表示にすることができます。

dotfilesにできないフォルダやファイルを非表示にしたい。特にSnapフォルダ

Ubuntuを使っていると、最近は snap フォルダができます。 snap フォルダは名前の変更がちょっとめんどくさいので、他の方法を探した。 環境変数を使わずに、手早く非表示にする方法があればいいなとおもったら、ありました。

snap フォルダを隠しフォルダにする。

これだけ。

echo snap > ~/.hidden

非表示になりました。

f:id:takuya_1st:20190807015626p:plain

.hidden を使って、好きなフォルダを非表示に

.hidden というファイルを使うことで、ドットファイルにできないフォルダでも非表示にすることができることがわかりました。

ArchWikiを読んでいて見つけました。

隠しファイル 他のファイルマネージャと同じように GNOME Files もデフォルトでファイルの名前がドットから始まるファイルを非表示にします。

さらに、GNOME Files ではファイルの名前が同一ディレクトリの .hidden ファイルに記載されていた場合、ファイルを表示しません (1行ごとにファイル名を記述します)。

任意のフォルダに .hidden が設置可能

.hidden ファイルは、任意のフォルダに設置して、任意のファイルをNautilusで不可視にすることができる。

これで、フォルダに普段使わないファイルやフォルダを保存していたとしても、よく使うものだけの表示にすることができる。これってすごく便利じゃないですかね。

こんかいは、 ~/snap フォルダに適用したけど、プロジェクトフォルダ、node_modules とかにも使えそうですね。

参考資料

GNOME Files - ArchWiki

0001docomo に PC/Mac から接続する- WAP2エンタープライズ

0001docomo の ドコモWifiMacから接続する。

docomoWiFiはめっちゃ優秀です。ガンガン使っていいレベルのインフラだと思います。 とくに、0001docomo は WAP2 エンタープライズなので、めっちゃ安全です。しかも通信速度も快適です。

接続方法

WPA2で接続するので、自動接続は使えない。

Macの場合は、「他のネットワークに接続」を明示的に選んで

f:id:takuya_1st:20190805184159p:plain

WAP2のユーザー名とパスワードを入れます。

f:id:takuya_1st:20190805184233p:plain

このID/PWは、マイドコモにログインして、接続情報から、WiFiパスワードを閲覧すれば、情報を得られます。

契約者であれば誰でも使えます。同じAPに同時に複数ログインはできない

iPhone/ mac の場合の問題点

WPA2のパスワードをMacが保存してKeychainに保存します。そのキーチェーンをiOSに同期してしまいます。 その結果、iPhoneEAP_SIM で動作せずにWAP2になってしまうので、iPhoneでドコモSIMかつキーチェーンを使っている場合は、パスワードを共有するApple仕様のために不便です。

接続情報はKeychainsに保存される。

次のように、キーチェーンに保存されます。このキーチェーンがMaciPhoneに同期されて、めんどくさいに事になります。

  • SIM認証が失敗する
  • Mac/ iPhone / iPad で接続の奪い合いになる。

f:id:takuya_1st:20190807182903p:plain

キーチェーンに保存しないでコマンドから接続するのが無難だと思います。

公衆無線LANは安全?危険

WPA2エンタープライズの0001docomo はユーザーごとに鍵交換を行うので、前提が違う。

「公衆無線LANは危険」とか区別もつかず暗黙的にお題目を言ってる人たちと一線を画している。

参考資料

docomo Wi-Fi:0001docomo - Windows PCを無線LAN接続

gas(google app script)でデザインや数式の入ったシートから値だけのシートにしてダウンロード

Google App Scriptを書いてみた

初めてのAppScriptだけど、すごく便利だったので、メモ

スプレッドシートから値だけを取り出してCSV的にする

スプレッド・シートは結構活用するのですが、スプレッドシートに数式や参照が含まれていて そのままコピペして送るのがちょっとあれだなと思ったので、スプレッドシートをコピーして数式を外した

Excelならコピーと貼り付けオプションでできるし、Gsuiteでももちろんできるんだけど

シートの枚数が多いのでめんどくさくなってきた。なのでGAS書いてみた。

gas であれこれやるサンプルになる

GASであれこれやるのに、参考資料として使えそうなので、資料を残すことに。

  • シートの作成
  • シートの選択
  • シートのコピー
  • ファイル形式変換して取得
  • メール送信

この辺のサンプルになるなと。

gas.js

function MailSpreadSheet(){
  
  // created_at : 2018-12-25 
  // auther     : takuya_1st 
  // description: 指定したファイルから数式とデザインを除外して、値だけのファイルにして、メールにして送る
  
  var file_id = "13OAD2yER60___GoogleDriveのファイルID____UMNw5MrSVA";
  var mail_to = "takuya@example.com";
  var subject = "実験-"+ (new Date()).toISOString();
  var body = "これは実験です。";
  var attachment_files =  [];
  
  var fetchOpt = {
    "headers" : { Authorization: "Bearer " + ScriptApp.getOAuthToken() },
    "muteHttpExceptions" : true
  };
  
  /// 新規スプレッドシートを作る。
  const createNewSpreadSheetFile = function (){
    // https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
    const name = (new Date()).toISOString()
    const app = SpreadsheetApp.create(name);
    // Logger.log(app.getUrl());
    return app
  }

  const CopyFileToNewFile = function ( src_file_id ){
    // 元ファイルを取得
    const src_doc = SpreadsheetApp.openById(src_file_id)

    // 新規ファイルを作成:シートのコピー先ファイルを追加。
    const dest = createNewSpreadSheetFile();  
    const dest_file = SpreadsheetApp.openByUrl(dest.getUrl());

    // すべてのシートについて
    //    作業用一時シートに値で貼り付けて
    //    新規ファイルへシートをコピー
    for ( idx in src_doc.getSheets() ){
      const sheet = src_doc.getSheets()[idx];
      const range = src_doc.getDataRange();
      
      // 新規シートを作って選択範囲をコピペ。
      const temp_sheet_name = (new Date()).toISOString();
      const temp_sheet = src_doc.insertSheet(temp_sheet_name);
      const temp_range = temp_sheet.getDataRange();
      range.copyTo(temp_range,{contentsOnly:true});
      // 新規ファイルへシートをコピー
      const dest_sheet =temp_sheet.copyTo(dest_file);
      dest_sheet.setName( sheet.getName() );//名前を元シートに合わせる。
      //一時的なシートを削除
      src_doc.deleteSheet(temp_sheet);
    }
    
    // 新規ファイルからファイル作成時の空白シート(先頭)を削除
    dest_file.deleteSheet( dest_file.getSheets()[0] );
    
    //コピーされたファイルを返す。
    return dest_file;
  }
  const fetchFileAsExcel = function( file_id ){
    const file = DriveApp.getFileById(file_id);
    const fileName = file.getName();
    xlsxName = fileName + "-変換-" + (new Date()).toLocaleString() + ".xlsx";
    //const blob = file.getAs( MimeType.MICROSOFT_EXCEL )
    //blob.setName(xlsxName);
    //return blob
    // もし blob が取れないときはURL経由
    fetchUrl = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + file.getId() + "&exportFormat=xlsx&format=xlsx";
    const blob = UrlFetchApp.fetch(fetchUrl, fetchOpt).getBlob().setName(xlsxName);
    return blob
  }
  
  const SendMail = function(){    
    
    MailApp.sendEmail(mail_to, subject, body, {attachments:attachment_files});
  }
 
  
  
  const main = function() {
    const file = CopyFileToNewFile(file_id);
    const blob = fetchFileAsExcel( file.getId() );
    
    DriveApp.removeFile(DriveApp.getFileById(file.getId()));
    attachment_files.push(blob);
    body = body + blob.getName();
    
    SendMail();
  }
  
  
  main();
}

サイト内検索を duckduckgo に切り替え

google のサイト内検索がどうもおかしい

記事はあるはずなのに、記事が出て来ない。

これが一定期間をすぎると検索結果から外されるというものでしょうか。

自分の過去記事をアーカイブとして利用しているとどうも、記事が見つからないことが増えてきた。

duckgo に試験的に変えてみる。

duckduck go でサイト内検索を設置するときは次のようにする

<form action="https://duckduckgo.com/">
  <input type="text" name="q">
  <input type="hidden" name="sites" value="example.com/sites">
  <button type="submit">Search</button>
</form>

しばらく様子を見てみたい。

参考資料

https://coderwall.com/p/0euadw/site-search-form-with-duckduckgo

gitでブランチ名を名前変更したい / ローカル branchのrename

git で ローカルブランチをtypo したとき、名前を変えたい。

isssue とか ちょっとした名前のミスであれば、名前の変更をしたい。

また、masterを直接更新 から git checkout -b name でブランチを後付で更新したいときに、名前をミスると、名称の変更が速い時がある。

ローカルのブランチの名前変更(1)

カレントブランチ(今いるブランチ)を変更

git branch -m  新しい名前

ローカルのブランチの名前変更(2)

変更前のブランチと変更後のブランチを指定して変更

git branch -m '古い名前'  '新しい名前'

branch -m / -M

ちなみに -m の m は移動の m です。rename というより コマンドのmv だと思ったほうがいいでしょう。move だと覚えておけば覚えやすいし忘れないです。

       -m, --move
           Move/rename a branch and the corresponding reflog.

       -M
           Shortcut for --move --force.

branch -m がおぼえられないひとむけ

何度やっても - m が覚えれない人は、ショートカットに登録しましょう

~/.gitconfig

[alias]
  rename-branch = branch -m

参考資料

man git-branch

git ブランチ作成と削除(ローカル)

git の使い方

今回は、git branch の操作方法。

ブランチは、もう説明が不要なくらい浸透してて嬉しい。

ブランチの作成と削除という基本概念をメモします。

ブランチ作成

今いるブランチから作成する

git checkout -b ブランチ名

いろいろあるけど、まずこれを覚える。

ブランチ削除

git branch -d ブランチ名

名前を指定して消します。

ブランチの一覧

git branch 

これでブランチ名がわかるので、ここから必要なものを消します。

でもめんどくさいの一括して消したいですよね・・・。

マージ済みのブランチをすべて消す。

git branch --merged | \grep -Ev  '\*|develop|master' | xargs git branch -d

git flow でよく使う maste /dev と、 いまのブランチ名* name だけを除外して全部消す。

alias に登録する。

これらを覚えたら。、一括して削除するコマンドをショートカットに登録する。

[alias]
   delete-merged = "!func () { git branch --merged| egrep -v  '\\*|master|develop' |xargs -I@  -t git branch -d @; }; func;"
git delete-merged

これで瞬殺できるから、お掃除できて楽ちん。

参考

man git branch