Linuxにおける2つのクリップボード - clipboard bufferとprimary selection

Linuxには「2種類のクリップボードのようなもの」があるということを今更明確に理解しました。

一つ目の「クリップボードのようなもの」は、Windowsなどでおなじみの"clipboard buffer"です。たとえばChromeにて、文字列を選択してCtrl + Cでコピー、Ctrl + Vでペーストという例のやつです。端末(Terminal)の場合はCtrl + C, Ctrl + Vがほかの用途で予約されているので、かわりにCtrl + Shift + Cでコピー、Ctrl + Shift + Vでペーストになります。

二つ目の「クリップボードのようなもの」は、"primary selection"とよばれるものです。例えば、端末にてマウスで文字列を選択したあと、Chromeの検索窓にてホイールクリックをするだけで、選択した文字列がコピーされます。

"primary selection"の方を意識して使うようにするとわずかながら作業速度が上がるかもしれません。

参考

シェルスクリプトでの'--'の意味は、オプションの打ち止め

シェルスクリプトでハイフンを2つ繋げた'--'の意味を最近はじめて知りました。これはオプションの打ち止めを意味します。

たとえばtouchコマンドで-hという空ファイルを作成しようとして

$ touch -h

とすると、-htouchコマンドのオプションとして解釈されてしまうため、下記のようにエラーが出てしまいます。

$ touch -h
touch: ファイルオペランドがありません
Try 'touch --help' for more information.

ここで--の出番です。--の後ろにある文字列はtouchのオプションではないことを明示できるので、以下のようにすることで-hという名前の空ファイルを生成できます。

$ touch -- -h 

書籍 "CUDA BY EXAMPLE" で julia_cpu.cu のビルドにはまる

引き続き、書籍 CUDA BY EXAMPLE を読み進めています。

Chapter 4のジュリア集合 - Wikipediaを描画するプログラムjulia_cpu.cuをビルドするのにやたら苦労したので、やったことをまとめておきます。OSはUbuntu 16.04 LTSです。

julia_cpu.cuのビルド

まず、gl/glut.hがシステム上に存在しないことからGLUTを入れる必要があることが分かったので、sudo apt-get install freeglut3-devでインストールしました。

次に、nvccコマンドで以下のようにビルドを試みますが、失敗します。

$ nvcc julia_cpu.cu -lglut
../common/cpu_bitmap.h(49): warning: conversion from a string literal to "char *" is deprecated

../common/cpu_bitmap.h(49): warning: conversion from a string literal to "char *" is deprecated

/usr/bin/ld: /tmp/tmpxft_00006f4f_00000000-10_julia_cpu.o: シンボル 'glDrawPixels' への未定義参照です
libGL.so.1: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

まず、共有ライブラリ周りに問題がないかを調べました。libglut.soをlocateコマンドで探すと、/usr/lib/x86_64-linux-gnuにあることがわかります。

$ locate libglut.so
/usr/lib/x86_64-linux-gnu/libglut.so
/usr/lib/x86_64-linux-gnu/libglut.so.3
/usr/lib/x86_64-linux-gnu/libglut.so.3.9.0

このlibglut.soが依存するライブラリをlddコマンドで追うと、すべてのライブラリに到達できているように見えます。(もし見つからない依存ライブラリがあれば"not found"とは表示される)

$ cd /usr/lib/x86_64-linux-gnu
$ ldd libglut.so
    linux-vdso.so.1 =>  (0x00007fff910f7000)
    libGL.so.1 (0x00007f476d237000)
    libX11.so.6 (0x00007f476cefd000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f476cbf4000)
    libXi.so.6 (0x00007f476c9e4000)
    libXxf86vm.so.1 (0x00007f476c7de000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f476c414000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f476c210000)
    libGLX.so.0 (0x00007f476bfe0000)
    libGLdispatch.so.0 (0x00007f476bd12000)
    libxcb.so.1 (0x00007f476baf0000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f476d723000)
    libXext.so.6 (0x00007f476b8de000)
    libXau.so.6 (0x00007f476b6da000)
    libXdmcp.so.6 (0x00007f476b4d4000)

次に、もう一度最初のエラーメッセージで検索をかけると、How to compile codes on CUDA OpenGL interop from the book CUDA BY EXAMPLE by Jason Sanders & Edw - NVIDIA Developer Forumsがヒットしました。これによると、-lGL -lGLUを加えるという回答があります。

最初は-lGLを追加から始めます。以下のコマンドを試しますが、今度はlibGL.soが見つからないと言われてしまいます。

$ nvcc julia_cpu.cu -lGL -lglut           
../common/cpu_bitmap.h(49): warning: conversion from a string literal to "char *" is deprecated
 
../common/cpu_bitmap.h(49): warning: conversion from a string literal to "char *" is deprecated

/usr/bin/ld: -lGL が見つかりません
collect2: error: ld returned 1 exit status

しかし、 /usr/lib/x86_64-linux-gnu/には libGL.soという名前のシンボリックが存在していたはずだから、libGL.soが見つからないと言われるのは心外です。このシンボリックリンクが指す先をfileコマンドで調べると、リンクが壊れていることがわかりました。

$ file /usr/lib/x86_64-linux-gnu/libGL.so
/usr/lib/x86_64-linux-gnu/libGL.so: broken symbolic link to mesa/libGL.so

なので、以下のように手動でlibGL.soのリンクを張り直しました。(注意:自己責任でお願いします)

$ sudo mv libGL.so libGL.manually-moved.so  
$ sudo ln -s libGL.so.1.0.0 libGL.so

このあともう一度サンプルをビルドすると、警告は出たままですが無事ビルドが通りました。

$ nvcc julia_cpu.cu -lGL -lglut     
../common/cpu_bitmap.h(49): warning: conversion from a string literal to "char *" is deprecated
 
../common/cpu_bitmap.h(49): warning: conversion from a string literal to "char *" is deprecated

苦労の末の戦果品として以下を捧げます。 f:id:minus9d:20180613204556p:plain

julia_gpu.cu のビルド

おまけで julia_gpu.cuのビルドについても触れておきます。プログラム中の

cuComplex( float a, float b ) : r(a), i(b)  {}

の行を

__device__ cuComplex( float a, float b ) : r(a), i(b)  {}

と書き換える必要がありました。(参考:c - CUDA Errors while running "CUDA By Example" julia_gpu.cu - Stack Overflow

書籍"CUDA BY EXAMPLE"の"../common/book.h"の場所

少し古い本ですが書籍"CUDA BY EXAMPLE"を読み始めました。

サンプルコードにある

#include "../common/book.h"

の場所が分からなかったのでメモ。現在ではCUDA By Example | NVIDIA Developerの "Download source code for the book's examples (.zip)" をダウンロードするとbook.hが入手できます。

ぐるっとパス2018用の地図

ぐるっとパス2018用の地図を作ってみました。

www.google.com

休館日だったり改装中だったりする可能性があるので、お訪ねの際はウェブサイトをご確認ください。

WindowsにてGhostscriptを使ってPDFのサイズを縮小する

ScanSnapにてA4サイズ32ページのカラー書類を600dpi設定で読み込むと、138MB程度のPDF (src.pdfとします) が生成されました。このままだと大きすぎて取り回しが悪いので、Windowsでサイズの縮小を試みました。用いたソフトはGhostscriptです。

GhostscriptはGhostscript: Ghostscript Downloads からインストールしました。バージョンは Ghostscript 9.23 for Windows (64 bit) です。

compression - How can I reduce the file size of a scanned PDF file? - Ask Ubuntu に5段階の縮小方法が記述されていたので、これを以下のバッチファイルで試します。

@echo off

set PATH=%PATH%;"C:\Program Files\gs\gs9.23\bin"

gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen   -dNOPAUSE -dQUIET -dBATCH -sOutputFile=01_screen.pdf   src.pdf
gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook    -dNOPAUSE -dQUIET -dBATCH -sOutputFile=02_ebook.pdf    src.pdf
gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/printer  -dNOPAUSE -dQUIET -dBATCH -sOutputFile=03_printer.pdf  src.pdf
gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/prepress -dNOPAUSE -dQUIET -dBATCH -sOutputFile=04_prepress.pdf src.pdf
gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default  -dNOPAUSE -dQUIET -dBATCH -sOutputFile=99_default.pdf  src.pdf

Ghostcript PDF Reference & Tips — Milan Kupcevic によると、-dPDFSETTINGSの各設定の意味は以下のとおりです。

-dPDFSETTINGS=/screen   (screen-view-only quality, 72 dpi images)
-dPDFSETTINGS=/ebook    (low quality, 150 dpi images)
-dPDFSETTINGS=/printer  (high quality, 300 dpi images)
-dPDFSETTINGS=/prepress (high quality, color preserving, 300 dpi imgs)
-dPDFSETTINGS=/default  (almost identical to /screen)

バッチファイルを実行した結果得られたPDFのサイズは以下のようになりました。

設定 サイズ
元PDF (src.pdf) 138MB
/screen 3.1MB
/ebook 7.2MB
/printer 52MB
/prepress 74MB
/default 138KB

/screen, /ebookを使った場合は画質があまりに荒く、とても見れたものではありませんでした。/printer, /prepressなら許容範囲だと感じました。

次に、より柔軟に圧縮率を指定したいと思い、compression - How to reduce the size of a pdf file? - Ask Ubuntu の回答を参考に、DPIを数値で指定しようとしたのですが、なぜか縮小されませんでした。一応以下にスクリプトを載せます。

@echo off

set PATH=%PATH%;"C:\Program Files\gs\gs9.23\bin"

gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages  -dCompressFonts=true -r150 -sOutputFile=150.pdf src.pdf
gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages  -dCompressFonts=true -r300 -sOutputFile=300.pdf src.pdf
gswin64.exe -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/default -dNOPAUSE -dQUIET -dBATCH -dDetectDuplicateImages  -dCompressFonts=true -r600 -sOutputFile=600.pdf src.pdf

Pipenvを使ってみる

Pythonが公式に推薦しているという、PythonのパッケージングソフトであるPipenvを試してみました。Pipenvは、Requestsの開発者としても有名なKenneth Reitz氏が中心となり開発されています。

例えばPythonでアプリケーションを開発するとき、複数の開発者がそれぞれ自分の手元で独立にpipでパッケージをインストールすると、そのタイミングによって異なるバージョンのパッケージが得られてしまう恐れがあります。このPipenvはその問題を解決し、開発者間で必ず同じバージョンのパッケージが使われるようにするためのツールだと自分は理解しています。

このPipenvは、pipとvirtualenvのラッパーとして働くので、pipとvirtualenvを直接さわることはなくなります。

Pipenvのインストール

GitHub - pypa/pipenv: Python Development Workflow for Humans. を参照。

使い方

まず使ってみる

以下はUbuntu 16.04 LTS on Windowsで試しました。PythonにはAnaconda 3を使うよう設定しています。

まず作業用のディレクトリを作成します。

$ mkdir myproject
$ cd myproject

次に、このプロジェクトで使うパッケージをインストールします。ここではrequestsパッケージを入れてみます。

$ pipenv install requests
Creating a virtualenv for this project…
Using /home/minus9d/anaconda3/bin/python (3.6.3) to create virtualenv…
...()...
Installing collected packages: idna, chardet, certifi, urllib3, requests
Successfully installed certifi-2018.4.16 chardet-3.0.4 idna-2.6 requests-2.18.4 urllib3-1.22

Adding requests to Pipfile's [packages]…
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Updated Pipfile.lock (b14837)!
Installing dependencies from Pipfile.lock (b14837)…
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 5/5 — 00:00:06
To activate this project's virtualenv, run the following:
 $ pipenv shell

このコマンドにより2つのことが行われました。

1つ目は、仮想環境の生成です。Pipenvが裏でvirtualenvを使って、このプロジェクトのための仮想環境が生成されました。

2つ目は、Pipfile, Pipfile.lockという2つのファイルの生成です。生成場所はmyprojectディレクトリの直下です。これらのファイルは、このプロジェクトが依存する他のパッケージの取得元やそのバージョンに関する情報を格納します。ファイルの内容に関する詳細は GitHub - pypa/pipfile にあります。

従来、プロジェクトが依存するパッケージの管理には、requirements.txtというのが使われていました。例えばpip install -r requirements.txtなどとすると、requirements.txtに記述された依存パッケージをインストールできました。今回生成されたPipfile, Pipfile.lockは、従来のrequirements.txtにとってかわるものになるようです。これら2ファイルはいずれも構成管理ツールの対象になります(参考)。

Pipfileは以下のようになります。requestsのバージョン指定をしなかったので"*"になっています。

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
requests = "*"

[dev-packages]

[requires]
python_version = "3.6"

Pipfile.lockは以下のようになります。こちらには実際にインストールされたバージョンが記述されています。

$ cat Pipfile.lock
{
    "_meta": {
        "hash": {
            "sha256": "8739d581819011fea34feca8cc077062d6bdfee39c7b37a8ed48c5e0a8b14837"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.6"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "certifi": {
            "hashes": [
                "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
                "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
            ],
            "version": "==2018.4.16"
        },
        "chardet": {
            "hashes": [
                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
            ],
            "version": "==3.0.4"
        },
  ...(略)...
        "requests": {
            "hashes": [
                "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
                "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
            ],
            "index": "pypi",
            "version": "==2.18.4"
        },
        "urllib3": {
            "hashes": [
                "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
                "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
            ],
            "version": "==1.22"
        }
    },
    "develop": {}
}

依存関係の可視化

pipenv graphとすると、これまでに入れたパッケージの依存関係が可視化されて表示されます。具体的には、requestsが要求するパッケージ、そのパッケージのインストール条件、実際にインストールされたパッケージのバージョンが表示されます。

$ pipenv graph
requests==2.18.4
  - certifi [required: >=2017.4.17, installed: 2018.4.16]
  - chardet [required: <3.1.0,>=3.0.2, installed: 3.0.4]
  - idna [required: <2.7,>=2.5, installed: 2.6]
  - urllib3 [required: >=1.21.1,<1.23, installed: 1.22]

仮想環境に入る

pipenv shellとすると、自分で入れたパッケージだけが使える仮想環境に入れます。出るときはexitです。

$ pipenv shell
Spawning environment shell (/bin/zsh). Use 'exit' to leave.
. /home/minus9d/.local/share/virtualenvs/myproject-yAY6_O7R/bin/activate
...(略)...
$ pip list
Package    Version
---------- ---------
certifi    2018.4.16
chardet    3.0.4
idna       2.6
pip        10.0.1
requests   2.18.4
setuptools 39.1.0
urllib3    1.22
wheel      0.31.0

上でもちょっと出てますが、インストールされたパッケージの場所はpipenv --venvによりわかります。

$ pipenv --venv
/home/minus9d/.local/share/virtualenvs/myproject-yAY6_O7R

デフォルトで ~/.local/share/virtualenvsの下に格納されるようです。知らない間に肥大化させないよう注意したいです。

他人が作ったPipfile, PipFile.lockに従いパッケージをインストール

Githubなどからレポジトリをcloneしてきて、そのレポジトリにPipfile, Pipfile.lockがあるような場合を考えます。この場合、pipenv install とすることで、レポジトリ製作者が使っているのと同じバージョンの依存パッケージをインストールできます。

$ git clone https://(somewhere)/myproject.git
$ cd myproject
$ pipenv install

仮想環境の削除

仮想環境を削除するときは、対象のディレクトリ(ここではmyproject)内で pipenv --rmとします(参考)。

$ pipenv --rm

これにより~/.local/shareに保存されていたパッケージファイルが削除されます。

requirements.txt の出力

pipenv lock -rとすると、requirements.txtと同形式の出力を得られます。

$ pipenv lock -r
-i https://pypi.org/simple
certifi==2018.4.16
chardet==3.0.4
idna==2.6
requests==2.18.4
urllib3==1.22

Anaconda Pythonのcondaとの接続

私は普段Pythonの環境構築にはAnaconda Pythonを使っています。その大きな理由の一つが、NumPyなどとくにWindowsでビルドするのが大変なパッケージについて、conda install numpyなどとconda installコマンドを使うことでビルド済バイナリを取得できることです。

今回紹介したPipenvでinstallコマンドを使うと、デフォルトではPyPIからパッケージが入手されるため、手元でパッケージのビルドが走ってしまうはずです。Pipenvの進んだ使い方 — pipenv 11.10.1 ドキュメント によると--site-packagesフラグを使えばcondaで入手したパッケージを再利用できるそうなのですが、私の手元ではうまくいきませんでした。

個人的には、ここが解決できない限り、Pipenvには移行できなさそうです。

[2018/05/24追記] 実は最近のNumPyなど多くのパッケージはwheel形式で配布されているためビルドなしでインストールできることが多いようです。なので実はPipenvを使う障壁は少ないのかもしれません。

参考URL