シェルスクリプトでディレクトリ配下を検索した結果を、フォルダ階層を保ったまま別ディレクトリにコピーする

2023-02-15Bash,CentOS,Linux,Ubuntu

はじめに

タイトルがわかりにくいので、早速、具体的な例を上げて説明します。

EC-CUBEのソースコードを拝借し、 Tag という文字を名前に含むファイルを、階層構造を保ったまま別のフォルダ ( /tmp/ec-cube ) にコピーしてみます。

検証環境

$ uname -moi
x86_64 MacBookPro10,1 Darwin

$ bash -version
GNU bash, バージョン 5.0.2(1)-release (x86_64-apple-darwin18.2.0)
Copyright (C) 2019 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

準備

$ git clone https://github.com/EC-CUBE/ec-cube.git

$ ls -l
drwxrwx--- 40 genzouw staff 1280  2 20 22:43 ec-cube/

やりかた

Tag という文字を名前に含むファイルは以下のコマンドで見つけられます。

# 一部のファイルだけに絞り込まれて出力されている
## -type f => ファイルのみを対象。ディレクトリは非対称。
## -name => Tagという文字を含むもののみを対象
$ find ec-cube -type f -name '*Tag*'
ec-cube/tests/Eccube/Tests/Web/Admin/Product/TagContorllerTest.php
ec-cube/tests/Eccube/Tests/DependencyInjection/Compiler/AutoConfigurationTagPassTest.php
ec-cube/src/Eccube/Form/Type/Admin/ProductTag.php
ec-cube/src/Eccube/Form/Type/Admin/TagType.php
ec-cube/src/Eccube/Repository/ProductTagRepository.php
ec-cube/src/Eccube/Repository/TagRepository.php
ec-cube/src/Eccube/Entity/ProductTag.php
ec-cube/src/Eccube/Entity/Tag.php
ec-cube/src/Eccube/Controller/Admin/Product/TagController.php
ec-cube/src/Eccube/DependencyInjection/Compiler/AutoConfigurationTagPass.php

これを階層構造を保ったまま /tmp/ec-cube にコピーするには、 tar コマンドを使います。

$ find ec-cube -type f -name '*Tag*' -print0 | tar --null -c --files-from=- -f - | (cd /tmp; tar xf -)

パット見わけがわからないが、なれます。(僕はなれはしないが、historyに残っているので流用しているw。)

コマンドの構成は、大きく2つの部分に別れています。
以下のように記述すると少しだけ見やすくなります。

$ (find ec-cube -type f -name '*Tag*' -print0 | tar --null -c --files-from=- -f -) \
  | (cd /tmp; tar xf -)
  1. 1段目
    1. find で検索した結果を標準出力に流す。
    2. tar コマンド は標準入力から対象ファイルを受け取り、圧縮。圧縮した結果を更に標準出力に流す。
  2. 2段目
    1. 展開対象のディレクトリに移動。
    2. 移動した先で標準入力から tarball データを受け取り、展開。

1段目find コマンドのオプション -print0tar コマンドの --null は、今回のケースでは指定しなくても正常に動作します。
ファイル名やディレクトリ名にスペースが入っている場合にはこのオプションが必要です。
find の出力結果、 tar の入力区切り文字としてデフォルトのスペースからヌル文字に変更してやる必要があります。

結果を確認してみる

# ディレクトリは新たにできている
$ cd /tmp/ec-cube/

# treeコマンドがあったので、構成を確認してみたところ確かにコピーされている
$ tree -f
.
├── ./src
│   └── ./src/Eccube
│       ├── ./src/Eccube/Controller
│       │   └── ./src/Eccube/Controller/Admin
│       │       └── ./src/Eccube/Controller/Admin/Product
│       │           └── ./src/Eccube/Controller/Admin/Product/TagController.php
│       ├── ./src/Eccube/DependencyInjection
│       │   └── ./src/Eccube/DependencyInjection/Compiler
│       │       └── ./src/Eccube/DependencyInjection/Compiler/AutoConfigurationTagPass.php
│       ├── ./src/Eccube/Entity
│       │   ├── ./src/Eccube/Entity/ProductTag.php
│       │   └── ./src/Eccube/Entity/Tag.php
│       ├── ./src/Eccube/Form
│       │   └── ./src/Eccube/Form/Type
│       │       └── ./src/Eccube/Form/Type/Admin
│       │           ├── ./src/Eccube/Form/Type/Admin/ProductTag.php
│       │           └── ./src/Eccube/Form/Type/Admin/TagType.php
│       └── ./src/Eccube/Repository
│           ├── ./src/Eccube/Repository/ProductTagRepository.php
│           └── ./src/Eccube/Repository/TagRepository.php
└── ./tests
    └── ./tests/Eccube
        └── ./tests/Eccube/Tests
            ├── ./tests/Eccube/Tests/DependencyInjection
            │   └── ./tests/Eccube/Tests/DependencyInjection/Compiler
            │       └── ./tests/Eccube/Tests/DependencyInjection/Compiler/AutoConfigurationTagPassTest.php
            └── ./tests/Eccube/Tests/Web
                └── ./tests/Eccube/Tests/Web/Admin
                    └── ./tests/Eccube/Tests/Web/Admin/Product
                        └── ./tests/Eccube/Tests/Web/Admin/Product/TagContorllerTest.php

20 directories, 10 files

応用編

find コマンド実行箇所を変更すれば、別の条件で絞り込みを行うことが可能。

直近1日以内に変更が行われたものをコピー

$ find ec-cube -type f -mtime -1 -print0 | tar --null -c --files-from=- -f - | (cd /tmp; tar xf -)

ファイルサイズが100KB以上のものをコピー

$ find ec-cube -type f -size +100k -print0 | tar --null -c --files-from=- -f - | (cd /tmp; tar xf -)

Gitの変更履歴から直近2コミットで変更されたものをコピー

$ mkdir -p /tmp/ec-cube

$ cd ec-cube

$ git diff --name-only HEAD\^\^..HEAD | tar -c --files-from=- -f - | (cd /tmp/ec-cube; tar xf -)

ひとこと

一部のファイルだけを人に渡したりするときに便利。

2023-02-15Bash,CentOS,Linux,Ubuntu