Linux コマンドで隠しファイルを含んで再帰的に移動・コピーする

Linux コマンドの cpmv で、隠しファイルを含んで複数のファイルを再帰的に移動したりコピーしたりしたかった。具体的には、

/PATH/TO/example-directory/
└ source/
   ├ .dotfile-1
   ├ example-file-1.txt
   └ sub-directory/
      ├ .dotfile-2
      └ example-file-2.txt

こんな感じで source/ ディレクトリ配下に隠しファイル (ドット . 始まりのファイル) がある状態で、これらを

/PATH/TO/example-directory/
├ source/  ← (移動した場合は空っぽ・コピーの場合はそのまま)
├ .dotfile-1
├ example-file-1.txt
└ sub-directory/
   ├ .dotfile-2
   └ example-file-2.txt

こんな風に一階層上に移動 or コピーしたいなー、と思っていた。

最初に思いつくのは

$ cp -r ./source/* .

こんな書き方だが、コレだと source/sub-directory/.dotfile-2 はコピーされるが、source/ 直下にある source/.dotfile-1 がコピーされないようだった。

そこで調べてみると、以下のいずれかの書き方で、source/ 直下にある source/.dotfile-1 も含めて再帰的にコピーできるようだった。

# 以下のいずれかのコマンド
$ cp -r ./source/. .
$ cp -a ./source/. .

なんと、アスタリスク source/* ではなく、ピリオド source/. を使うようだ。

検証した限りでは、以下のコマンドは微妙だった。

# 以下の書き方は「一階層上に上げる」時に使うと `..` もコピー対象に扱われてしまいエラーメッセージが出てしまったので微妙
# 別のディレクトリにまるっと移動するなら使えそう
$ cp -r ./source/.[^.]* ./dist/

# 直下の source/.dotfile-1 のみを対象にする書き方
$ cp -r ./{.,}* .

なお、cp -rcp -acp コマンドでしか使えない。mv の場合は別の方法を使う。

cp でも mv でも両方で使えるやり方として、アスタリスクが隠しファイルを含むように shopt コマンドで設定を変えておく、というやり方がある。コレの方が分かりやすいかも。

# 予め以下の shopt -s コマンドを打っておく
$ shopt -s dotglob

# そうすると、以下のようにアスタリスクを使った cp・mv 時にドットファイルも含んで操作してくれる
$ cp -r ./source/* .
$ mv ./source/* .

# shopt -s コマンドによる設定を元に戻すには以下のように叩く
$ shopt -u dotglob

読んで字の如く、dotglob はドットファイルも glob 展開の対象にする、というワケ。

ちなみに、cpmv には -n オプションがあり、コレを指定するとコピー先・移動先に同名ファイルがあった時に上書きコピー・移動をしなくなる。うっかり上書きしてしまわないようにするには、-n オプションを使うと良いだろう。