シェル(1)Bashの基本設定

Bashとは

シェルは、コマンドライン・インタプリタとも呼ばれ、ユーザが入力したコマンドをカーネルに渡すプログラムである。シェルには、Bashcshzshなど様々な種類が存在するが、LinuxやMac OSでは、Bashが標準シェルとなっている。/bin/shは、Linuxにおける標準シェルを示しており、通常はBashへのシンボリックリンクとなっている。

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4  9月 26 15:52 2014 /bin/sh -> bash

なお、Bashを/bin/shとして実行させるとPOSIX準拠モードで起動するため、厳密にはBashと挙動が異なる。以下のコマンドを実行することで、インストール済みのシェルを確認することができる。

$ cat /etc/shells 
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh

ファイルディスクリプタ

シェルは、実行するプログラムの以下の値を取得することが可能となっている。

ディスクリプタ 略記 内容
<0 < 標準入力
>1 > 標準出力
>2 >2 標準エラー出力

標準出力も標準エラー出力も同時に出力したい場合は、2>&1と記述する。

ショートカット

カーソルの移動や編集を行う際に以下のショートカットを使うと便利。

コマンド 意味 内容
Ctrl + a ahead 行頭に移動
Ctrl + e end 行末へ移動
Alt + b before 一単語左へ
Alt + f foward 一単語右へ
Ctrl + d delete カーソルの文字を消す
Ctrl + h 0x08 (Backspace) カーソルの左の文字を消す
Ctrl + w word カーソルの左の単語を消す
Ctrl + u unix-line-discard カーソルより左の文字を全てカット
Ctrl + k kut カーソルより右の文字を全てカット
Ctrl + y yank ペースト
Ctrl + p previous 前の履歴を表示
Ctrl + n next 次の履歴を表示
Ctrl + r reverse 過去の履歴から検索
OLDPWD 直前にいたディレクトリ

設定ファイルの読み込み順序

bashの設定ファイルはいくつもあるが、以下の順序で読み込まれるらしい。

読み込み順序 読み込み時 対象 ファイル
1 ログイン時 全ユーザ /etc/profile
2 ログイン時 各ユーザ ~/.bash_profile
3 ログイン時 各ユーザ ~/.bash_login
4 bash起動時 各ユーザ ~/.bashrc
5 ログイン時のシェル終了時 各ユーザ ~/.bashrc

PATHを通すときなどに無意識に~/.bashrcにPATHを書いてたけど、~/.bashrcにPATHを書いてbashを複数タブで起動すると、その度に同じPATHが追加されてしまうようだ。というわけで、設定ファイルは、~/.bash_profileに書いておけばいいということか。

設定ファイルの内容

historyコマンド時に時間情報も表示させると便利。
過去のコマンドが消えてしまわないように、historyの上限値も増やしておくと、なお便利。
ちなみに、![履歴番号]で、history内の過去のコマンドを実行できる。

# History
HISTSIZE=50000
HISTTIMEFORMAT='%Y-%m-%d %H:%M:%S '

# Path
PATH="$PATH":/usr/local/bin:/bin:/sbin:/usr/bin

コマンドの実行

シェルで実行ファイルを実行する場合、PATH付きで指定しなければいけない。カレントディレクトリの実行ファイルを、

$ ./a.out

などと書くのは上記ルールによる。ただし、シェルにPATHが通っている場合は、PARHを省略できる。実行したファイルのPATHを確認するにはwitchコマンドを使って、

$ witch ls

alias ls='ls --color=auto'
	/bin/ls

とすればよい。また、cd, echo, pwdなどのコマンドは、ビルトインコマンドと呼ばれ、シェル自身に組み込まれたコマンドである。コマンドのタイプを確認するためには、

$ type pwd
pwd is a shell builtin

typeコマンドを使用する。

AWS S3(2)S3Sync:S3バックアップツール

S3Sync

以前、S3にバケットを作成してGlacierアーカイブを行う手順を確認したが、この仕組みを利用してMacの任意のディレクトリをS3 Glacierと自動的に同期するアプリケーション「S3Sync」を作ってみた。Macのスリープを検知すると同期を始めるので、寝ている間にラクラク同期できる。

といってもこのアプリ、単にNSTaskを使ってシステムコマンドを実行しているだけのアプリなので、任意のコマンドを自由に実行することができる。ステータスバーに常駐しているアプリなので、作業の邪魔にもならない。

S3Sync

スリープ検知

スリープ検知をするには、NSWorkspace ClassNSWorkspaceWillSleepNotification属性を使う。

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // スリープ検知
        NSWorkspace.sharedWorkspace().notificationCenter.addObserver(self, selector: #selector(self.receiveSleepNotification(_:)), name: NSWorkspaceWillSleepNotification, object: nil)
    }

    func receiveSleepNotification(notification: NSNotification){
        // スリープ実行時に行う処理
    }

システムコマンドの実行

システムコマンドは、NSTask Classから実行することが可能である。
NSTaskは、実行したシステムコマンドの出力結果を取り出すことも可能だが、readDataToEndOfFile()を使うとブロッキング処理が発生してしまうので、dispatch_async()を使って非同期に順次出力処理していく必要がある。

let task = NSTask() 
// 実行コマンドをフルパスで指定
task.launchPath = "/usr/local/bin/aws "
// パラメータを配列形式で指定
task.arguments = ["-h", "hogehoge"]
// 標準出力をパイプに渡す
let pipe: NSPipe = NSPipe()
task.standardOutput = pipe
let stdoutHundle = pipe.fileHandleForReading     
// 非同期処理
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), {
var dataRead = stdoutHundle.availableData
while(dataRead.length > 0){
	let stringRead = NSString(data: dataRead, encoding: NSUTF8StringEncoding)
        if let output = stringRead {
		// 出力結果処理
        }
        dataRead = stdoutHundle.availableData
}
// コマンドの実行
task.launch()

NSTaskは、suspend()terminate()を使って、途中で処理を停止したり、完全に終了してしまったりすることができる。suspend()で中断した処理は、resume()で再開することができる。また、タスクが実行中にも関わらず再度launch()を実行してしまうと、下記の実行エラーが発生してしまう。

task already launched

通知の送信

本アプリは、コマンド実行毎にMacの通知センターに実行状況を通知する。

アプリ通知

Macの通知センターに通知を送信するには、NSUserNotificationCenter Classを使う。送信する通知には、「タイトル」「サブタイトル」の他に様々な項目を設定することが可能である。

// NSUserNotificationCenterDelegateが必要
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {

    func deliverNotification(title : String, subtitle : String, informativeText: String){
        // AppDelegate Classにデリゲードを指定
        NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self
        let notification = NSUserNotification()
        notification.title = title
        notification.subtitle = subtitle
        notification.informativeText = informativeText
        notification.contentImage =  NSImage(named: "MainIcon")
        notification.userInfo = ["title" : "タイトル"]
        NSUserNotificationCenter.defaultUserNotificationCenter().deliverNotification(notification)
    }

}

ログを保存するディレクトリの指定

本アプリは、実行ログをファイルに保存することができる。

NSOpenPanel

Macでディレクトリやファイルを開くする際は、NSOpenPanel Classを利用する。
また、保存の際はNSSavePanel Classというクラスも用意されている。

// MARK: ディレクトリ選択画面
let panel = NSOpenPanel()
// ファイル選択の可否
panel.canChooseFiles = false
// ディレクトリ選択の可否
panel.canChooseDirectories = true
// 複数選択の可否
panel.allowsMultipleSelection = false
panel.beginWithCompletionHandler({(num) -> Void in
      if num == NSModalResponseOK {
           // ディレクトリ・ファイル決定時の処理
      }
})

常駐アプリ

ステータスバーに常駐するアプリを作成するためには、Project > TARGET > Info > Custom OS X Application Target Propertiesから、Application is agent (UIElement)YESに設定する。

Application is agent

StoryBoardのTips

  • 常駐アプリであっても、Main Menu > Edit がないと、TextFieldの Shortcut Keyが使えない

Main Menu : Edit

機械学習(1)TensorFlowをMacにインストールする

TensorFlowは、Googleが開発し公開している機械学習ライブラリ。

インストール方法

Pythonパッケージ管理ツール「easy_install」を使うとよいとのことなので、以下のコマンドを実行。このとき、XCodeのコマンドラインツールのインストールも求められる。

sudo easy_install pip
sudo easy_install --upgrade six
sudo pip install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.8.0-py3-none-any.whl

tensorflow-0.8.0-py3-none-any.whl is not a supported wheel on this platform.

TensorFlowを入れるためにはpip3(python3)が必要とのこと。
brewからpython3のインストールを行う。

このときpythonのバージョン管理を行うことのできるpyenvを入れておくと、Python2.7とPython3を切り替えることができるので便利。

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install pyenv

現在のPythonバージョンを確認することができる。

pyenv versions
* system (set by /Users/***/.pyenv/version)

まだ何も入れていないので、systemのみ。

ここで、~/.profileファイル(ログイン時に読み込むbash設定)に以下を追加。
設定を読み込ませるためにOS再起動。

vi ~/.profile

export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"

インストール可能なバージョンを確認して、Python3(今回は3.5.1)をインストール。

pyenv install --list
pyenv install 3.5.1

使用するPythonのバージョンを3.5.1に変更。

pyenv global 3.5.1
pyenv versions
  system
* 3.5.1 (set by /Users/eiji/.pyenv/version)

このときにもし以下のようなエラーが発生する場合は、

Downloading Python-3.5.1.tgz...
-> https://www.python.org/ftp/python/3.5.1/Python-3.5.1.tgz
Installing Python-3.5.1...

BUILD FAILED (OS X 10.11.4 using python-build 20160130)

Inspect or clean up the working tree at /var/folders/my/txp4p33n1lzf2hbzp0vmqbc40000gn/T/python-build.20160515224141.3027
Results logged to /var/folders/my/txp4p33n1lzf2hbzp0vmqbc40000gn/T/python-build.20160515224141.3027.log

Last 10 log lines:
  File "/private/var/folders/my/txp4p33n1lzf2hbzp0vmqbc40000gn/T/python-build.20160515224141.3027/Python-3.5.1/Lib/ensurepip/__main__.py", line 4, in <module>
    ensurepip._main()
  File "/private/var/folders/my/txp4p33n1lzf2hbzp0vmqbc40000gn/T/python-build.20160515224141.3027/Python-3.5.1/Lib/ensurepip/__init__.py", line 209, in _main
    default_pip=args.default_pip,
  File "/private/var/folders/my/txp4p33n1lzf2hbzp0vmqbc40000gn/T/python-build.20160515224141.3027/Python-3.5.1/Lib/ensurepip/__init__.py", line 116, in bootstrap
    _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
  File "/private/var/folders/my/txp4p33n1lzf2hbzp0vmqbc40000gn/T/python-build.20160515224141.3027/Python-3.5.1/Lib/ensurepip/__init__.py", line 40, in _run_pip
    import pip
zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

XCodeのコマンドラインツールもインストールする。

xcode-select --install

最後に、TensorFlowをインストール。

sudo pip3 install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.8.0-py3-none-any.whl

インストールされたかどうかの確認は、以下のコマンドを打ってエラーが発生しないことを確認する。

python

Python 3.5.1 (default, May 15 2016, 09:29:31) 
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

import tensorflow as tf 

Pythonプログラムをデーモン起動する(Raspbian)

Pythonで実装したプログラムを、Raspberry Pi(Raspbian)でデーモン起動させるときのメモ。

python-daemonのインストール

python-daemonは、Pythonプログラムをdaemon化できるパッケージである。

sudo apt-get install python-daemon

デーモンプログラム

Pythonプログラムはこんな感じ。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import serial
import time
 
from daemon import DaemonContext
from daemon.pidlockfile import PIDLockFile
 
if __name__ == '__main__':
        # 以下がデーモンプロセスとして実行される
        # pidfileが/tmpにないと、動作しないらしい
        with DaemonContext(pidfile=PIDLockFile('/tmp/serial-arduino.pid')):
                main()

起動スクリプト

サービス起動させるための起動デーモンは以下の通り。

$ sudo vi /etc/init.d/hoged
 
#!/bin/sh
 
### BEGIN INIT INFO
# Provides:        hoged
# Required-Start:  $local_fs $remote_fs $syslog
# Required-Stop:   $remote_fs
# Default-Start:   2 3 4 5
# Default-Stop: 
# Short-Description: Start hoged daemon
### END INIT INFO
 
PATH=/sbin:/bin:/usr/sbin:/usr/bin
# Source function library.
. /lib/lsb/init-functions
 
# Path to the script
DAEMON=/usr/local/sbin/python-hoge.py
PROG=serial-arduino
PIDFILE=/tmp/hoged.pid
LOCKFILE=/tmp/hoged.pid.lock
 
test -f $DAEMON || exit 0
 
lock_hoged() {
        if [ -x /usr/bin/lockfile-create ]; then
                lockfile-create $LOCKFILE
                lockfile-touch $LOCKFILE &
                LOCKTOUCHPID="$!"
        fi
}
 
unlock_hoged() {
        if [ -x /usr/bin/lockfile-create ] ; then
                kill $LOCKTOUCHPID
                lockfile-remove $LOCKFILE
        fi
}
 
case "$1" in
    start)
        log_daemon_msg "Starting $PROG server" "$PROG"
        lock_hoged
        start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -p $PIDFILE
        status=$?
        unlock_hoged
        log_end_msg $status
        ;;
    stop)
        log_daemon_msg "Stopping $PROG server" "$PROG"
        start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
        log_end_msg $?
        rm -f $PIDFILE
        ;;
    status)
        status_of_proc -p $PIDFILE $DAEMON $PROG
        ;;
    restart)
        stop
            start
        ;;
    *)
        echo "Usage: $PROG {start|stop|status|restart}"
        exit 1
        ;;
esac

最後に変更を反映。

sudo update-rc.d hoged defaults

Raspberry Piでスクリーンショットを撮影する

Raspberianでスクリーンショットを撮るときは、Scrotがオススメ。

Scrotをインストール

$ sudo apt-get install scrot

ウインドウを指定してスクリーンショットを撮るときは、
sオプションを付けて実行し、該当のウインドウをクリックする

$ scrot -s

以上でスクリーンショットを撮影することができる