それマグで!

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

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

tail コマンドがファイルの削除、作成時でも動くようにする

tail -f コマンドを使うとファイルの削除で動作が止まる。

tail -f /path/to/log

で ログファイルを見ているときに、別のプロセスでファイルが消えたら

ファイルが消えたら、動作が止まる。

mv /path/to/log /dev/null
rm /path/to/log 
echo hello > /path/to/log 

これはファイルディスクリプタだけを見ているので、ファイルが消えるとログを追尾できない

-F / –follow=name を使う

tail -F /path/to/log
tail --follow=name a.log

これでパス名のファイルの有無などを監視してくれるようになる。

tail -f は inotify や fsevent を見てると思うので、基本的には -F で良い気がする。

man tail

-f, --follow[={name|descriptor}]
              output appended data as the file grows;

              an absent option argument means 'descriptor'

-F     same as --follow=name --retry

inotify を見てる話はman にそう書いてる。

      -s, --sleep-interval=N
              with -f, sleep for approximately N seconds (default 1.0) between iterations; with inotify  and
              --pid=P, check process P at least once every N seconds

tail -f コマンドがログ監視てるプログラムが死んだら一緒に自動的に終了する

pid を指定すると tail コマンドが自動的に終了する

tail -f --pid=$PID /path/to/log

PIDを指定したときに

ローカルホスト開発ツールのログ監視してるときとかに便利ですね。

man tail

       --pid=PID
              with -f, terminate after process ID, PID dies

参考資料

https://hydrocul.github.io/wiki/commands/tail.html

ChromeのDeveloper tools(開発ツール)のコンソール(コマンドライン)で出てくる記号と関数

chrome の dev tools に出てくる関数

$ と $$ の違い

$ = document.querySelector
$$ = document.querySelectorAll

$x は Xpath

$x('//a')

xpath は組み立てと呼び出しが面倒なので、とても嬉しい。

その他の関数をマトメておくと

クエリ系

  • $
  • $$
  • $x

直前のものを再利用

  • $_

選択された要素

  • $0
  • $1
  • $2
  • $3
  • $4

デバッグ

  • debug
  • monitor/unmonitor
  • table
  • copy
  • profile/profileEnd

要素やオブジェクトの検証

  • inspect

イベントハンドラの監視と調査

  • getEventListeners
  • monitorEvents/unmonitorEvents

参考資料

https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference?hl=ja#04

右クリックでEvernoteに送信する

右クリックでEvernoteにファイルを送りたい

Finderで右クリックでファイルをEvernoteに送るにはどうするか。

「このアプリケーションで開く」→「Evernote」とすればいいし、項目にEvernoteがなければ、その他から選べばいいいのですが。。。

最初から用意されてない拡張子が出てくるたびに登録するのが面倒くさい。UTI書き換えれば良いんだけどそれも面倒に感じた

右クリックメニューのサービス項目を作成した

f:id:takuya_1st:20170510154650p:plain

サービスを作ってすべてのファイルを大正にした。

前に作った コマンドを呼び出すようにする。

#!/bin/bash

path/to/file2evernote.js "$@"

右クリックメニューで出てくる

f:id:takuya_1st:20170510154828p:plain

これで気にせず使える。

ぱぱっとEvernoteにPDFやJPEGmarkdownCSSを放り込んで楽をすることが出来た。

Evernoteにファイルを投入するコマンド

Evernoteにファイルをまとめて放り込みたい。

コマンドでまとめてEvernoteに放り込みたい。APIを経由すると毎年〜キーを更新しなくちゃいけないので面倒くさい

なので、Apple Script ( の JavaScript ) でやろうと思う ( Windows な人は同じ目的で enscript.exe が使えます)

JavaScriptで自動化するときのポイント

コマンドを使って起動するときは

#!/usr/bin/env osascript -l JavaScript

ObjC.import('stdlib')  
/// ここがmain関数に相当する
function run( argv ){
  console.log("Hello World")
}

Evernote に新規ノートを作ってファイルを貼り付け

Evernoteに新規ノートを作ってファイルを貼り付ける場合は、次のようにする。

  var name = 'no title'
  var file_path = '/Users/takuya./.bashrc'
  var app = Application("Evernote")
  var notebooks = app.notebooks
  var defautNoteBook = Array.apply(null, notebooks).find(function(e){ return e.default })
  var note = app. createNote({
    title: name,
    notebook: defautNoteBook,
    withText : name
  })
  note.append({ attachment: Path(file_path) })
  app.activate()

NSString とFileManagerを使ってPATHの存在チェックなど

realpath と file_exist? をやって引数チェックする

file2evernote.js

ObjC.import('Cocoa')
nsStr = $.NSString.alloc.initWithUTF8String(file_path);
file_path = nsStr.stringByStandardizingPath.UTF8String;
mgr.fileExistsAtPath(e)

これをまとめてコマンドにする

引数で受け取ったファイルを一覧を処理してEvernoteに貼り付ける。

gist.github.com

使い方

./file2evernote.js *.jpg

JXAのJavaScript AutomationとObjc Cocoaのオブジェクト呼び出し例

JXA で NSString を使ってPATHの変換をしてみたのでメモ

objcのCocoa から NSString と FileManager を使ったので、いろいろ知見を得られたのでメモ。

Javascriptで、NSString を使うには、次のようにする。

ObjC.import('Cocoa')

NSStringを init して alloc する

## [[NSString alloc] init]
$.NSString.alloc.init

JS の文字列から NSString に変換する。

// [[NSString alloc] initWithUTF8String:"Hello world"];
nsStr = $.NSString.alloc.initWithUTF8String("Hello World.");

NSString から JS の文字列に変換する

JS文字列は基本的にObjC→C言語へ変換するのと同じ

// [@"Hello world" UTF8String]
var str = nsStr.UTF8String;

メソッドを呼び出す。

nsStr = $.alloc.initWithUTF8String("/Users/takuya/.bashrc")
nsStr = nsStr.stringByStandardizingPath
nsStr = nsStr.stringByStandardizingPath.UTF8String;

メソッドの呼び出しは CoCoa を書いてるときみたいに書けばいい。むしろ最後の丸括弧を除去した方がいいところが面喰らう的な。getter/setter なんかも同じ。

ポインタを引数に取るメソッドを使うとき、キーワード引数を使うとき

// - (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;

mgr = $.NSFileManager.defaultManager;
var isDir= Ref();
mgr.fileExistsAtPathIsDirectory(e,isDir) && isDir[0]

変数のポインタを扱うのは var ptr = Ref() で、デリファレンスするのは ptr[0] のように配列の中身を見る感じっぽい 同じ関数 fileExistsAtPath でもキーワード引数をつけるなら、引数を関数名にするのがみそらしい

参考資料

すごくわかり易い例がいっぱいあって助かりました。

http://palepoli.skr.jp/tips/javascript/jxa.php

Selenium で フォーム送信

Selenium で フォーム送信 するには。

若干のWaitを入れてあげるとうまく動くと思う。

driver.goto 'https://example.com/login'
wait = Selenium::WebDriver::Wait.new(:timeout => 3) # second
## ページロードを待つ
wait.until { driver.xpath('//*[@id="clogs"]/input[1]').displayed? }
## フォーム送信をするには send_keysを使う
driver.xpath('//*[@id="clogs"]/input[1]').send_keys('takuya***')
driver.xpath('//*[@id="clogs"]/input[2]').send_keys('***')
driver.xpath('//*[@id="clogs"]').submit

Selenium使おうとしたらchromedriverが古くてエラーになった。

selenium 使おうとしたらエラーになった。

>> require 'selenium-webdriver'
=> true
>> driver = Selenium::WebDriver.for :chrome , :desired_capabilities => caps
NameError: undefined local variable or method `caps' for main:Object
from (pry):2:in `__pry__'
>> driver = Selenium::WebDriver.for :chrome
EOFError: end of file reached
from
>>

EOFErrorになったので調べたら、 ChromeDriverが古い事が原因のようです。

バージョン確認

takuya@Desktop$ /Users/takuya/repos/my_command/usr/local/bin/chromedriver -v

バージョン確認したら 結構古かった。はずかしいのでバージョンは書かない。

brew でアップデート

brew でパッケージが提供されるようになったので楽ちん

takuya@Desktop$ brew install chromedriver
Updating Homebrew...
==> Auto-updated Homebrew!

アップデート後に再確認

>> require 'selenium-webdriver'
=> true
>> driver = Selenium::WebDriver.for :chrome
=> #<Selenium::WebDriver::Driver:0x..fa2af2f73e150f72c browser=:chrome>
>> driver
driver
>> driver
driver
>> driver.close
=> nil
>>

問題なく動く。EOF Error が バージョンだと気づくまでに1時間位かかった。迂闊だった。

tailf と grep を組合せて Slackにログを通知する。パイプでワンライナー

ログファイルを監視して、slack などに通知したい。

ログを監視して Slackに投げる方法で、かんたんなのを考えた。

ログファイルに何か変更があって、それが特定のときに メールや slack のチャンネルに通知を出したら便利だと思う。

あれこれログ監視や通知のソフトウェアやパッケージがあるけれど、一番大事なのは手軽で何も知らなくても使えることだと思う。

ファイルを監視して grep して HTTP POST するだけなら、パイプで十分に使える。

ログ監視の仕組み

要は、次のコマンドパイプを呼び出せればいい。ただしいくつかポイントが有るので、それを見ておく

目指す形

このエントリでは次のようなコマンドを作る

tail -f path/to/file | grep some_thing |  curl -X POST url

このままでは動かない。

コマンドの組合せならすぐ思いつきそうなパイプですが、単純にこのコマンドだけでは動きません。tai や grepcurl コマンドの特性上、このままではパイプで動かない。すこし変更が必要でした。

コマンドの問題点

tailf / tail -f ではログがローテーションしたときにOpenしたファイルを追尾しない。新しいログファイルを開いてくれない。そのため、このままでは動きませんでした。

grepは、そのままでは次のパイプがリアルタイムに起動しませんでした。

curl コマンドは、この書き方すると標準入力行を全部受け取ってしまいます。

これらを防ぐために若干のコツというか、オプションを調べて見直したので、まとめておきます。

tail -F でパスを監視する

tail コマンドには -F というオプションがあり、これで該当PATHのファイル監視を出来る。 もし tail -fで監視していると、ファイルが削除・移動後に追跡できない。そのため、tail を使った長期間ログ監視なら、次のコマンドが望ましい。

ログ監視の一番かんたんなコマンド

tail -F logs/lastest.log

追加された行だけを取得する

tail を起動すると、最終5行を表示して -f : follow モードに入る。そのため起動すると過去ログが何度も通知されるのが望ましくない。

tail -n0 -F logs/lastest.log

tail と grep の組合せ

tail -n0 -F logs/lastest.log | grep takuya

tail -f とコマンドの組合せが便利。

パイプ先で必要情報だけを取出して、残りを捨てたフィルリングや書き換えが出来る。

tail と grep を組合せれば、ログをフィルタできる。また-f / -Fをつけたtail は、パイプがブロックIOになるので、ログが更新にリアルタイムで追従してくれる。

tail | grepにさらにパイプをつなぐ

### リアルタイム動作しない
tail -n0 -F logs/lastest.log | grep  takuya  | grep login

tail | grep は正しくフィルタするが、 tail | grep | grep は画面出力が得られない。 なぜか?。

grep tail -f の組合せは、grep パイプ後パイプが動かない時がある。これはgrep がバッファリングしてしまう事が原因です。

実は grep が内部的にバッファリングして、高速化してくれる。そのため tail -f | grep | grepでは、中間のgrepがIOをバッファリングして、最後のgrepに即時にログが届かない。

line-buffered のオプション

次のオプションで、複数行バッファリングを一時停止(行単位で出力バッファ)するようにする。

man grep
      --line-buffered
              Use line buffering on output.  This can cause a performance penalty.

tail | grep | grep するときは次のようにする。

そのため tailf を grep して grep したり、sed したりする場合は、 次のようにする。

tail -F logs/lastest.log | grep --line-buffered takuya  | grep --line-buffered  login

ログを受け取ったら、何かする。

行単位でログがやってくる。

行単位で受け取ったらPOST処理をするとしたら、curl でPOSTしようとすると、行を標準入出力から受け取る必要がある.

出来ない

grep something | curl -X POST -d - ... 

grep した結果をcurl で送信しようとしても、curl で標準入出力をパイプで扱うと、1度しか起動できない。行毎に起動するには xargs を使う

xargs で行が来るたびに送信する。

 grep --line-buffered takuya  | xargs -I@ curl -X POST http://example.com/ -d message="@"

xargs を -I で1行単位で起動するようにして、@` という文字で指定して、1行をコマンドの文字列に展開する。

slack のURLを用意する

Slack にはPOST用の webhook が用意されていて、最近はチャンネル毎にPOST用のWebhook のURLを生成できる 生成したURLは次のように、ディレクトリが3段になっていた (2017/05/09)

https://hooks.slack.com/services/Axxx/Bxxx/Cxxx

ここに curl で post / json すれば、 Slack に拾われる。

curl -s  \
  https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \
   -X POST \ 
   -H 'Content-type: application/json' \
    --data '{"username":"curl", "text":"#{my message}"}'

パイプにまとめる。

ここまで見てきたそれぞれのコマンドとパイプを全部まとめると、次のようになる。

tail -n0 -F logs/lastest.log | grep --line-buffered takuya  |  xargs -I @ curl -s  \
  https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \
   -X POST \ 
   -H 'Content-type: application/json' \
    --data '{"username":"curl", "text":"@"}'

これで、ログが更新されたら、 grep で フィルタをかけ、条件にあったログが来たら Slack にPOSTすることが出来る。

さらにコレを関数か、シェルスクリプトファイルにする

このままでは、ファイルの指定とエンドポイントのPOSTのURLが変更が不便なのでシェルスクリプトにしておくと便利だと思います。

さらに、systemd に登録

これらのコマンド(シェルスクリプト)をsystemd.unitの service に登録して、Daemonと同時に起動しておけば、だいぶ楽だよね。

マインクラフトのログを監視する

たとえば、マインクラフトサーバーで「死んだ」人をSlackに晒し上げるには

ログファイルが lastest.log で、grep で slain を取れば死んだことが解り、最後にSlackで通知したら出来上がり。

tail -n0 -F logs/lastest.log | \
grep --line-buffered slain  | \
xargs -I @ curl -s  \
  https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \
   -X POST \ 
   -H 'Content-type: application/json' \
    --data '{"username":"minecraft-serverl", "text":"@"}'

コマンドで出来てるので、たとえば、死んだ回数や死因を累計とか、ログイン・ログアウトを監視してプレイ時間を通知するとかね。

まとめ

tail と grepcurl だけで、ログを監視して通知することが出来ます。

tail -F で ファイルのパスの状態を監視できます

grep –line-buffered で行ごとにgrep をすることが出来ます。

xargs curl で行の内容をPOSTすることが出来ます。

個人的な

ちょっとしたログ監視を調べたら、ちょっとね・・・

「最強の○○のログ監視」と「ログ監視パッケージをdocker コンテナ」を導入してる 某キ◯タさんの意識高すぎるエントリに辟易して調べました。

開発とかちょっとしたことにわざわざDockerコンテナだのAWSとか、ちょっと重たすぎだよね。手順やリソースが多すぎる。

ログ監視ってのは単一のログファイルを見て何かするのと、「複数サーバー(インスタンス)」のログを一箇所に集めて何かするのとだいぶ違うと思うんだよね。

Slackに投稿したり、ログを投げたり、まとめたりっていろいろな方法がある。パッケージを入れてしまえばたしかにかんたんなのですが。後始末が面倒になる。後始末が面倒になってたくさんのインスタンスを使い潰したりコンテナから起動したりで、時間や学習コストは抑えられるのですが。金銭的に不利だったり、学習コストを抑えたために、応用が効かなかったり。。。うーんって思うことも有る。

google chrome が google アカウントのパスワードを補完(入力しない問題

Google Chrome がパスワードを利用させてくれない

ChromeGoogleアカウントでログインしてる場合、そのアカウントのパスワードは補完されない。

そのために、Chromeでパスワードを何度も入力することになり面倒くさい。

原因と理由

ちょっと実験してみた。

Chromeブラウザにログインしてるアカウントのパスワードを「バックアップ同期」している場合に起きる。つまり、Chromeブラウザからログアウトするとパスワード補完は復活する。

なぜこの仕様、これが鬱陶しく面倒になるのか?ここ最近のGoogleアカウントはパスワードの再確認が頻繁に起きるようになった。いぜんは手軽にアカウントを切り替えなどが出来たのに。最近はアカウントを切り替えることも面倒くさい。

解決策1

Chromeをログインせずに使う。Chromeにログインせずに利用すれば、いかなるGoogle アカウントでもパスワード自動入力が動く。

解決策2

Chrome にログインしてる場合、パスワードの同期を外す。

これはChromeにログインして、拡張機能やブックマークやセッションなどをバックアップ同期したい場合に有効

[f:id:takuya_1st:20170507184544p:plain:300]

解決策3

Chromeにログインし同期する専用のアカウントを作る。

GmailやOAuthのログイン連携で常用するアカウントと、Chromeにログインしてバックアップを取るアカウントを分けてしまう。

参考資料

https://productforums.google.com/forum/#!msg/chrome/xjXc0i-bqNs/-js6NHZACAAJ

素因数分解をしてくれるコマンド factor

prime numbers ( 素数 ) に分解してくれるコマンドがあった。

takuya@Desktop$ factor 123456
123456: 2 2 2 2 2 2 3 643

factor とは 要因の意味で数学的に因数で、factor コマンドは 素数を使って因数を表現してくれるコマンド

何に使うのか。

特に何に使うわけでもないけれど、コマンド呼び出しやexec 系やパイプの実験のサンプルに丁度いいと思います。

takuya@Desktop$ irb
>> exec 'factor 10'
10: 2 5

標準入出力を使った例

コマンドはブロッキングIOで逐次処理されるので、IOを使った実験に向いている。

takuya@Desktop$ factor
43
43: 43
22
22: 2 11
11
11: 11
1111
1111: 11 101

このようにコマンドの実行サンプルの例として使うのに向いてるのかもしれない。

何処に入ってるのか?

coreutils に入っています。

mac なら 次のようにするとインストール出来ます。

brew install coreutils

シェルスクリプトの改行コードを修正する

改行コードが原因でエラーになる。

改行コードを \r\n が原因でエラーになった。

takuya@:~$ bash -n apt
apt: 行 2: 予期しないトークン `$'\r'' 周辺に構文エラーがあります
'pt: 行 2: `_apt()

一瞬何のエラーかわからなかったけどよく見るとエラーに改行文字が含まれてた。

改行コードを修正するコマンド dos2unix

インストール

sudo apt install dos2unix

実行

dos2unix an.sh

とても便利ですね

似たような仲間には

  • /usr/bin/dos2unix
  • /usr/bin/unix2dos
  • /usr/bin/unix2mac
  • /usr/bin/mac2unix

などがあります。CRLF : dos/windows 、 LF : UNIX / bsd / linux , OS X(macOS) , CR : Mac OS(9) などですね。

過去資料

改行コードを置換する専用コマンド - それマグで!

標準出力と標準エラーのリダイレクト(コピー)について

標準出力と標準エラー出力を扱うときに

たまに間違って 1 というファイルが出来てしまって悲しいことになる。

takuya@Desktop$ ll 1
-rw-r--r-- 1 takuya staff 20 2017-04-27 14:51 1

これを防ぐためにちょっと標準出力と標準エラーの扱いをまとめておこうと思う。

標準出力とエラーの出力先の転送(コピー)について

最初に、エラーと出力をするかんたんなプログラムを用意しておきます。

cmd.rb

$stdout.puts "to stdout"
$stderr.puts "to stderr"

普通に実行した場合

takuya@~$ ruby cmd.rb
to stdout
to stderr

エラー出力の転送先を指定する。

takuya@~$ ruby cmd.rb 2>/dev/null
to stdout

標準出力の転送先を指定する。

takuya@~$ ruby cmd.rb 1>/dev/null
to stderr

両方の出力先を指定する。

takuya@~$ ruby cmd.rb 1> stdout.log 2>stderr.log
takuya@~$

画面には何も出ない。

ここまでのおさらいとさらなること

なんとなくわかってるようで、ここまでをシッカリ覚えた上で、どうなってるかをもう少し突っ込んでみてみないと失敗する。

プロセスの実行は、STDOUT/STDERRをそれぞれ繋いで(ファイルディスクリプタのコピー)になることを覚えておく。左が変数名、右が変数の中身(FDが指している先)

cmd.rb の fd 中身(出力先)
1 stdout
2 stderr

エラー出力を /dev/null に

エラー出力を捨てた場合。

takuya@~$ ruby cmd.rb 2>/dev/null
fd 中身(出力先)
1 stdout
2 /dev/null

上記の様に考えると、次からの転送をもう少し理解できる。

標準エラー出力と標準出力を同じにする

エラーと標準出力を合流させる。

takuya@~$ ruby cmd.rb 2>&1
to stdout
to stderr

これはこう考えると良い

fd 中身(出力先)
1 stdout
2 stdout

fd2 に fd1 の内容がコピー(同じに)された。

標準エラーを合流させて、標準出力をファイルへ

takuya@~$ ruby cmd.rb 2>&1 1>/dev/null
to stderr

これは、次のように一つずつの処理が2回動作している。そう考えると理解できる。

fd 中身(出力先)
1 stdout
2 stdout stderr

&1 で 1が指している stdout を2へコピーしている。

fd 中身(出力先)
1 /dev/null stdout
2 stdout

1 が指している内容を /dev/null に書き換えた

標準出力をファイルに出して2つを合流させる

takuya@~$ ruby cmd.rb > /dev/null 2>&1
takuya@~$

これは、次のように一つずつ、処理が2回動作していた。

そう考えると理解しやすい

1個めの処理

fd 中身(出力先)
1 /dev/null stdout
2 stderr

2個めの処理

1 が指している内容を /dev/null に書き換える

fd 中身(出力先)
1 /dev/null
2 /dev/null stderr

&1 で 1が指している内容(/dev/null) を2へコピーしている。

出力先を入れ替える.。

よくあるお題の、出力先の入れ替えも見てみる。

標準エラー出力と標準出力を入れ替えるには?」をここまでの表で見ておくことにする。

この状態を

fd 中身(出力先)
1 stdout
2 stderr

こうしたい

fd 中身(出力先)
1 stderr
2 stdout

つまりコピーしてしまうと。。。

takuya@~$ ruby cmd.rb  2>&1 1>&2
to stdout
to stderr
最初の指定 2>&1 でこうなり
fd 中身(出力先)
1 stdout
2 stdout stderr
2番目の指定 1>&2 でこうなる
fd 中身(出力先)
1 stdoutstdout
2 stdout

あぅ、、、だめじゃんおなじになっちゃう。

コピーするなら、一旦tmp を経由する。

追記コピーのようなことは出来ないので、いったん別の場所に対比しておく

任意のfd へ

ruby cmd.rb  3>&2

最初の状態の以下を書き換えたい

fd 中身(出力先)
1 stdout
2 stderr
3

こうした

fd 中身(出力先)
1 stdout
2 stderr
3 stderr
次にfd2 を書き換える
ruby cmd.rb  3>&2 2>&1
fd 中身(出力先)
1 stdout
2 stderr
3 stderr
最後に、fd1 を書き換える。
ruby cmd.rb  3>&2 2>&1 1>&3
fd 中身(出力先)
1 stderr stdout
2 stdout
3 stderr
出来上がり
fd 中身(出力先)
1 stderr
2 stdout
3 stderr

リダイレクトの指定順に注意する

1を〇〇へ、2を〇〇へとおぼえてしまうと、間違うことが多いので、 1 の指している先を2へコピーなど、言葉を少し変えて記憶したほうが無難でしょうね。

&1 記号の意味

2>&1 
2>/dev/null

これは &11 の指している先 を示すポインタで、 ファイル名の代わりに使っているだけだと考えて

2 >  &1 
2 >  /dev/null

このように敢えてスペースを入れて、対比させると覚えやすい ( >& にスペースを入れられないのだけど、スペースを入れ対比したほうが直感的)

まれに間違って教えてる人や間違って覚えてしまう人がいて

1 と 2 を合流させるから &アンド だとか、1 and 2 で 1 の内容に2 をプラス(追加)するなどと簡便な説明をしてしまってあとで混乱してしまうことになる。

省略記法

1と2の出力先をどちらも /dev/null にしてしまうなら

ruby cmd.rb   >& /dev/null
ruby cmd.rb   &> /dev/null
ruby cmd.rb   &>> /dev/null

此のような書き方も出来る。 此の書き方であれば、1+2を合流したと説明しても差し支えないと思う。

まとめ

## 標準出力の fd1 の指定は省略できる
ruby cmd.rb   1> /dev/null
ruby cmd.rb   > /dev/null

## 両方の出力先を同じにする
ruby cmd.rb   2>&1
ruby cmd.rb   1>&2

## 両方の出力先を同じしてファイルへ
ruby cmd.rb  >/dev/null  2>&1
ruby cmd.rb  1>/dev/null  2>&1
ruby cmd.rb  2>/dev/null  1>&2
ruby cmd.rb   >& /dev/null
ruby cmd.rb   &> /dev/null
ruby cmd.rb   1> /dev/null 2>/dev/null
ruby cmd.rb   1> /dev/null 2>/dev/null


## 出力先を入れ替える。
ruby cmd.rb   3>&1 1>&2  2>&1
ruby cmd.rb   3>&2 2>&1 1>&2 

github issue のラベルを編集する

github issue のラベルを編集してフレンドリーにする

github のIssueのラベルを編集して名前をつけたら楽しい

https://github.com/$(repo_name)/labels

編集した結果

f:id:takuya_1st:20170427142816p:plain

ラベルをアレコレつけると感情が伝わってフレンドリーになる。

ただし、ラベルも使いすぎるとワケガワカラナイヨってなるので、締め切り毎のラベルを作るようなことはせずに、マイルストーンでやったほうが良いと思った。

参考資料

Issuesのラベルを管理(編集)する方法 - 22時に寝ようと思って2時に寝る。

Bashでシンボリックリンクのディレクトリの補完(上位ディレクトリが辿れない

シンボリックリンクされたディレクトリから上位が辿れない・・・

シンボリックリンクされたディレクトリがあって、その内部から、補完を試みても、ディレクトリがうまく補完されない。

takuya@vim$ ll  -d /usr/local/opt/vim
lrwxr-xr-x 1 takuya admin 26 2017-04-24 14:32 /usr/local/opt/vim -> ../Cellar/vim/HEAD-b7637c4
takuya@vim$ cd  /usr/local/opt/vim
takuya@vim$ cd ../<TAB>#押しても反応なし

解決策(一時的)

complete -r cd  

cd の補完で、ディレクトリに限定するのが complete cd なのです。シンボリックリンクはファイルなので補完されない。。

ディレクトリではなく、全ファイル(+ディレクトリ)を補完対象にすると一時的にはうまくいく。

ただし、これはcd の補完をオフにしているだけなので完璧とはいえない。

complete -r cd  # 補完オフ
complete -d cd  # ディレクトリ名で補完

解決策( bash-completion )

bash-completion で cd の補完を独自に定義するとまぁ、なんとかなる。

流石にコレを全部書くのはちょっと・・・なので、最初から用意されている _cd を少しカスタマイズする

type _cd  # この結果をカスタマイズする
complete -F _cd cd 

具体的にはこうする

function_cd_with_symlinks(){
  local cur opts;
  cur="${COMP_WORDS[COMP_CWORD]}";
  _cd; ## 既存の _cd を呼び出しておいて
  opts="${COMPREPLY[@]} path/to/c";      ## 追加する
  COMPREPLY=($(compgen -W "${opts}" -- ${cur})); 
}

解決策(諦める)

シンボリックリンクの解釈を諦める。

set -P

これで、cd を含めすべてのPATHの取扱が、シンボリックリンクにきたら物理的なアドレスに変えてくれる。

これと言った解決策がない

cd が解決したとしても ls findなどのコマンドはやっぱりシンボリックリンクではなくreadlink後の物理パスに対して作用するので、どうしようもない感じで詰んだ。

参考資料

https://askubuntu.com/questions/155696/how-to-tab-complete-directories-when-in-symlinked-directory

http://stackoverflow.com/questions/26550327/bash-completion-to-make-cd-command-complete-working-directories-from-other-run

Bash completion to make 'cd' command complete working directories from other running shells? - Stack Overflow