セットアップスクリプトなどでmkdir,cp,chown,chgrp,chmodが頻発するときにシンプルに記述する方法

Bash, Docker, Linux

はじめに

アプリケーションのセットアップスクリプトDockerfileVagrantfile を記述しているときに、以下のようなコマンドが乱立することがあります。

  • mkdir
  • cp
  • chown
  • chgrp
  • chmod

mkdir でコピー先のディレクトリを作成し、 cp でファイルをコピー、 chown / chgrp で所有者を変更し、 chmod でパーミッションを変更、といった記述をしだすと、4コマンドは実行することになります。

これを1コマンドでやってしまおうというのが今回のエントリです。

検証環境

今回は非常に軽量なDcokerイメージである alpine でも使えることを確認したいため、以下の環境を利用しました。

$ uname -moi
x86_64 unknown Linux

$ sh --help 2>&1 | head -n 1
BusyBox v1.30.1 (2019-06-12 17:51:55 UTC) multi-call binary.

ちなみに「Alpine Linux」は docker コマンドが利用できれば簡単に起動できます。

$ docker run -i -t --rm alpine

4つのコマンドを1コマンドで実現するためのコマンド install

答えとしては非常に簡単で、 install コマンドを利用します。

利用できるディストリビューション

いくつかのディストリビューションを確認しましたが、いずれもインストールされているようでした。

# CentOS
$ docker run -i -t --rm centos
[root@6742f69c602b /]# install --version | head -n 1
install (GNU coreutils) 8.22

# Ubuntu
$ docker run -i -t --rm ubuntu
root@854017b7cc5f:/# install --version | head -n 1
install (GNU coreutils) 8.28

# Debian
$ docker run -i -t --rm debian
root@4c81dcfa3f3f:/# install --version | head -n 1
install (GNU coreutils) 8.30

# Alpine
$ docker run -i -t --rm alpine
/# install 2>&1 | head -n 1
BusyBox v1.30.1 (2019-06-12 17:51:55 UTC) multi-call binary.

Macの場合は brew install gnutls あたりでインストールできると思います。(僕の環境はすでにインストールされていた。)

使い方

ヘルプを確認してみます。

$ install --help
Usage: install [OPTION]... [-T] SOURCE DEST
  or:  install [OPTION]... SOURCE... DIRECTORY
  or:  install [OPTION]... -t DIRECTORY SOURCE...
  or:  install [OPTION]... -d DIRECTORY...

cp コマンドとよく似ていて、 install コピー元ファイル コピー先ファイル のように使います。

オプションも見ていきましょう。
今回利用するオプションのみに絞って表示させてみました。

  -D                  create all leading components of DEST except the last,
                        or all components of --target-directory,
                        then copy SOURCE to DEST
  -g, --group=GROUP   set group ownership, instead of process' current group
  -m, --mode=MODE     set permission mode (as in chmod), instead of rwxr-xr-x
  -o, --owner=OWNER   set ownership (super-user only)
  -p, --preserve-timestamps   apply access/modification times of SOURCE files
                        to corresponding destination files

それぞれのオプションの役割を日本語で簡単に表現すると以下のようになります。

  • -D : mkdir -p と同じ。ディレクトリがない場合に作成する。
  • -g : chgrp と同じ。グループを変更する。
  • -m : chmod と同じ。パーミッションを変更する。
  • -o : chmod と同じ。所有者を変更する。
  • -p : cp --preserve=timestamps と同じ。タイムスタンプを保持する。

使用例

実際に利用してみます。

検証のために、コピー元ディレクトリ src/ と コピー先ディレクトリ dest/ を作成し、コピー元ディレクトリにコピーファイル hello.txt を作成します。

以下のコマンドを見ていただければ、わかると思います。

$ mkdir -p src dest

$ echo hello > src/hello.txt

$ tree
.
├── src
│   └── hello.txt
└── dest

2 directories, 1 file

ここで、 src/hello.txtdest/ ディレクトリにコピーしたいと思います。
ただし、以下の条件を用意したいと思います。

  • 所有者は root
  • グループは guest
  • パーミッションは 700 (所有者だけがアクセス可能)
  • コピー後のファイルパスは dest/sub/world.txt

install コマンドを使わない場合は以下のようになります。

# ディレクトリを作成
$ mkdir -p dest/test/

# コピー
$ cp --preserve=timestamps src/hello.txt dest/sub/world.txt

# 厳密に言うと、このあたりの処理でタイムスタンプが変わってしまうか?
#   * ちなみに `chgrp` は `chmod` だけでも実現できます。
$ chmod root dest/sub/world.txt
$ chgrp guest dest/sub/world.txt
$ chmod 777 dest/sub/world.txt

install コマンドを使う場合はワンライナーで実現できます。

$ install -o root -g guest -m 700 -D src/hello.txt dest/sub/world.txt

正しくコピーできたか確認してみます。

$ ls -l dest/
drwxr-xr-x    2 root     guest          4096 Jan 21 01:22 sub

$ ls -l dest/sub
-rwx------    1 root     guest             6 Jan 21 01:22 world.txt

ひとこと

DcokerfileVagrantfile が更にシンプルに書けるようになるはず。
次回作成時に使ってみたいと思います。

Bash, Docker, Linux