Linux向けポータブル環境の基本

# Linux向けポータブル環境の基本

**[【ぜんぶポータブル】ポータル](https://qiita.com/jtFuruhata/items/f9788da8e480c646a2a9)**

**最終更新:2019年3月6日**

【ぜんぶポータブル】環境の「Linux」では、**Ubuntu**と**Raspbian**、**Debian GNU/Linux**で`APT`(dpkg)の利用を前提として説明されています。[^他dist]
ただし、標準状態ではポータブルデバイス上のバイナリファイルを実行することができませんので、その対応を学習寄りで説明します。

[^他dist]: 他系列のディストリビューションの方はすみません、いつものように適宜読み替えてください。

## いや説明はいいです、一言でお願いします。
ポータブルデバイスにある実行ファイルを、`/tmp`など権限のある場所に`rsync -tr --chmod=755`してPATHを通してください。

これでわかる方は以下の説明は読まなくて大丈夫。
【ぜんぶポータブル】する方は[スクリプトだけ持っていってください](#パスを通すスクリプト)。

## 確認に使用した環境

- Ubuntu
    - VMware Fusion 11.0.2
    - Ubuntu 18.04.2 LTS 日本語 Remix リリース
        - amd64版をインストールし、アップグレードのみを済ませた状態で仮想マシンのスナップショットを取り、色々と確認する
    - ユーザー名は`jt`、もちろん読み替えてください
- Raspbian
    - Raspberry Pi 3 Model B V1.2
    - Raspbian Stretch with desktop and recommended software（2018-11-13）
    - ユーザー名は`pi`
- ポータブルデバイスはFAT32でフォーマットされ、`/media/jt/USBMEM`にマウントされている
- ポータブル環境フォルダは`/media/jt/USBMEM/jtHiuPortable`にある
- こんな感じでやりました。

<img width="1136" alt="DebianPortable_01.png" src="https://qiita-image-store.s3.amazonaws.com/0/151581/f8468ae9-9743-e5b9-aa9f-b8f39ac15b8d.png">

## ポータブル環境の準備
操作はすべて`端末`（または`LXTerminal`）を使います。`Ctrl`+`Alt`+`T`キーで起動します。
また、テキストエディタは`nano`で説明しますがもちろん`vi`でもなんでもいいです。

### ディレクトリを掘って移動する
あとでスクリプト化しますがとりあえず。
1行目のパスは自分の環境に置き換えてください。

```bash:Bashコマンド
export HOME="/media/jt/USBMEM/jtHiuPortable"
export P_CMD="$HOME/apps/linux"
mkdir -p "$P_CMD"
cd "$P_CMD"

```

### アーキテクチャを調べる
Linuxカーネルのアーキテクチャは`arch`コマンドで、Debianパッケージのアーキテクチャは`dpkg --print-architecture`コマンドで取得できます。今回の動作環境ではこうなっています。

||Linuxカーネル|Debianパッケージ|
|:--:|:--:|:--:|
|VMware|x86-64|amd64|
|RasPi3|armv7l|armhf|

ポータブル環境構築で大事なのはDebianパッケージ上でのアーキテクチャ名です。
そんなわけで以降、デスクトップPCの名前は`amd64`、ラズパイの名前は`armhf`だと思ってください。

### パスを通すスクリプト

パスを通すためのシェルスクリプトを書きます。

```bash:Bashコマンド
touch setPath.sh
nano setPath.sh

```

```bash:setPath.sh
#!/bin/bash
if [ -v $HOME_ORG ]; then
    export HOME_ORG="$HOME"
fi
if [ -v $PATH_ORG ]; then
    export PATH_ORG="$PATH"
fi

pwd_org="$PWD"
cd "`dirname $0`/../.."
export HOME="$PWD"
cd "$pwd_org"
export P_CMD="$HOME/apps/linux"

export TMPDIR="/tmp"
export P_ROOT="$P_CMD/`dpkg --print-architecture`"
export T_ROOT="$TMPDIR/zenbuPortable"
export P_BIN="$P_ROOT/usr/bin"
export T_BIN="$T_ROOT/usr/bin"
export P_LIB="$P_ROOT/usr/lib"
export T_LIB="$T_ROOT/usr/lib"
export PATH="$T_BIN:$PATH_ORG"

mkdir -p "$P_BIN"
mkdir -p "$T_BIN"
mkdir -p "$P_LIB"
mkdir -p "$T_LIB"

rsync -rt --chmod=755 $P_BIN/ $T_BIN/

echo Portable home has located in $HOME
echo Portable applications have located in $P_ROOT

```

`nano`を保存して終了するときは、
`Ctrl`+`O` → `Enter` → `Ctrl`+`X`
と順番に押します。

### 環境変数の解説
スクリプト処理での順に、

- `HOME_ORG` ... 元々のホームディレクトリです。
- `PATH_ORG` ... 元々のPATHです。
- `HOME` ... ポータブル環境のホームディレクトリです。
- `P_CMD` ... ポータブル環境実行コマンドの格納場所です。
- `TMPDIR` ... ポータブル環境の一時ファイル格納場所です。`/tmp`以下に設定して、**再起動で自動的に削除**してもらいます。
- `P_ROOT` ... ポータブル環境ルートです。Debianパッケージのアーキテクチャ名で掘っています。
- `T_ROOT` ... 一時ファイル格納場所のルートです。
- `P_BIN` ... ポータブル環境ルートの`/usr/bin`です。
- `T_BIN` ... 一時ファイル格納場所の`/usr/bin`です。
- `P_LIB` ... ポータブル環境ルートの`/usr/lib`です。
- `T_LIB` ... 一時ファイル格納場所の`/usr/lib`です。
- `PATH` ... 実行ファイルが見つからない場合はここ（`T_BIN`）を探しに行きます。多重呼び出しで無限増殖しないように設定しています。

で、関連ディレクトリを準備する、というものです。

### ポータブル環境でのスクリプト実行
さて、このスクリプトを動作させたいところですが、ここはLinuxの世界です。
Macの場合はFAT系デバイスを接続すると、パーミッションは777固定として扱われるのですが、
**Debianでは644固定**、つまり実行権を付与できません。
や、もちろんやろうと思えば普通に出来ますけど、極力標準インストールで`sudo`さんに頼らないでやるというのが【ぜんぶポータブル】のコンセプトですので。

ただし、MacのFinderダブルクリック同様、

**拡張子が`.command`のファイルには自動的に実行権が付与されます**。

これはUbuntuもRaspbianも同様です。
そんなわけで、上記スクリプトを実行して、環境変数が適用された状態で`Bash`を開くための`.command`ファイルを作成します。

```bash:Bashコマンド
touch startConsole.command
nano startConsole.command

```

```bash:startConsole.command
#!/bin/bash
. `dirname $0`/setPath.sh
cd "$HOME"
bash

```

これを実行してパスを設定し、さらに、ポータブル環境フォルダのトップに、ファイルマネージャからダブルクリックで起動できるようコマンドを作成します。
（Macで既に作った方は不要です）

```bash:Bashコマンド
./startConsole.command

# （パスを適用したBashが起動する）

touch Console.command
nano Console.command

```

```bash:Console.command
#!/bin/bash
if [ `uname` == "Darwin" ]; then
    platform="mac"
elif [ `uname` == "Linux" ]; then
    platform="linux"
else
    platform="win"
fi
`dirname $0`/apps/$platform/startConsole.command

```

`uname`がDarwinかLinuxかそれ以外（MSYSとかMinGWとか）かでプラットフォームを判別しています。

### ファイルマネージャから起動する
デスクトップ環境とファイルマネージャは、Ubuntu（GNOME+Nautilus）とRaspbian（PIXEL+PCManFM）でちょっと違い、ダブルクリックしてスクリプトを起動するには、

- Ubuntu（Nautilus）
    - 初期設定では、ダブルクリックするとテキストエディタが起動する。
    - `端末`で起動させるには、「ファイル」メニューから`設定`を選び、「動作」タブ中「実行可能なテキストファイル」から`どうするか確認する`を選択する。
    - 以降ダブルクリックするとダイアログが出るので、`端末内で実行する`ボタンを押す。
- Raspbian（PCManFM）
    - 「ファイルを実行する」ダイアログが出るので、`端末で実行する`ボタンを押す。

いずれもダブルクリック一発、とは行かないのですが、まぁご愛嬌ということで。

あっ、あとUbuntuではコマンドを実行するたびにいちいち「sudoさんにお願いしなさい」と口酸っぱく言われますが**無視して結構です**。

## 基本的な考え方

現状どのようなコマンドが使えて、それがどの実行ファイルを呼び出すかは、`which`コマンドで調べることができます。

>```bash:端末
>jt@ubuntu:~$ which rsync
>/usr/bin/rsync
>```

`rsync`は`/usr/bin/rsync`にあることがわかります。
これをUSBメモリにコピーします。

```bash:Bashコマンド
cp `which rsync` $P_BIN

```

もちろん、USBメモリ上でバイナリファイルは実行できませんので、アクセス権を設定できる一時ファイル格納場所に持っていきます。

では実際に試してみます。

>```:端末
>jt@ubuntu:~$ which rsync
>/usr/bin/rsync
>jt@ubuntu:~$ rsync -tr --chmod=755 $P_BIN/ $T_BIN/
>jt@ubuntu:~$ which rsync
>/tmp/zenbuPortable/usr/bin/rsync
>```

`setPath.sh`で既にPATHを通してありますので、`rsync`は一時ファイル格納場所の方を使うようになりました。

ここでポータブル環境を抜けると、

>```:端末
>jt@ubuntu:~$ exit
>exit
>jt@ubuntu:~$ which rsync
>/usr/bin/rsync
>jt@ubuntu:~$ ls /tmp | grep zenbu
>zenbuPortable
>```

環境変数PATHの変更が無効となり、元通りになります。
ただし、一時ファイル格納場所は残ったままです。

では、おもむろに再起動して、もう一度端末を開いてください。

>```:端末
>jt@ubuntu:~$ ls /tmp | grep zenbu
>jt@ubuntu:~$ which rsync
>/usr/bin/rsync
>```

何も表示されない。`/tmp`は再起動で自動的に消去されます。

で、ファイルマネージャから`Console.command`をダブルクリックして`端末内で実行する`と、

>```:端末
>Portable home has located in /media/jt/USBMEM/jtHiuPortable
>Portable applications have located in /media/jt/USBMEM/jtHiuPortable/apps/linux/amd64
>To run a command as administrator (user "root"), use "sudo <command>".
>See "man sudo_root" for details.
>
>jt@ubuntu:~$ which rsync
>/tmp/zenbuPortable/usr/bin/rsync
>jt@ubuntu:~$ 
>```

このように、元々のインストール状態には一切迷惑をかけずにポータブル環境のアプリケーションを実行しよう、というのが趣旨です。

確認できましたら、実際には`rsync`はポータブルにしないので、削除しておきます。

```bash:Bashコマンド
rm $P_BIN/rsync

```

基本的な考え方としてはこんな感じです。