Linux勉強会 第4回：ファイル操作とディレクトリ操作

*この記事は、[株式会社Ancar](https://ancar.co.jp/)で行っている勉強会のおさらい & 外部発信記事として公開されています。

まえがき
---
こんにちは、Webエンジニアの[Mass-min](https://twitter.com/masumi_sugae)と申します。
社内で始まったLinux勉強会も気づけば今回で第4回。Linuxﾁｮｯﾄﾃﾞｷﾙようになってきたかな！？(ﾁｮｯﾄﾃﾞｷたら苦労はしない🙄)
[第1回](https://qiita.com/yungo/items/8f9cb967f0bd5fa35242)からずっと勉強してきたファイル操作に加えて、今回はディレクトリ操作についても学んでいきますよ〜。お品書きはこんな感じです。それでは今回も頑張っていきましょう！:tada: 

前提
---
OS: macOS Catalina(10.15.3)
シェル: bash

おしながき
---
- ファイル操作
  - ファイルの日付を変更する( touch コマンド)
  - 現在の日時の表示・設定( date コマンド)
- ディレクトリ操作
  - ディレクトリ内容の表示( ls コマンド)
  - ディレクトリの作成( mkdir コマンド)
  - ディレクトリの削除( rmdir コマンド)
  - ディレクトリの移動と名前変更( mv コマンド)

ファイル操作
---
### ファイルの日付を変更する( touch コマンド)
[第1回](https://qiita.com/yungo/items/8f9cb967f0bd5fa35242#3touch)でも触れたように、touchコマンドは本来ファイルの更新日時を変更するためのコマンドです。 `man`コマンドでtouchコマンドのマニュアルを見てみると、たしかに「ファイルアクセス日時とファイル変更日時の変更をする」って書いてありますね。

```:man&nbsp;touch
TOUCH(1)                  BSD General Commands Manual                 TOUCH(1)

NAME
     touch -- change file access and modification times
```

touchコマンドの使い方やオプションはいろいろありますが、その中でも代表的な使い方をおさらいしていくことにします。

#### 現在の日時に修正する
現在の日時に修正するには、以下のようにします。

```:touchで更新日時を現在の日時に修正する
$ touch ファイル名
```

```
$ ls -l afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:25 afile
```

この状態から、afileの更新日時を現在時刻にしてみましょう。

```
$ date
2020年 2月27日 木曜日 23時59分46秒 JST
$ touch afile
$ ls -l afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:59 afile
```

変更できてますね！🥳

#### 指定の日時に修正する
指定の日時に修正するには、`t`オプションを使います。

```:touchで更新日時を指定の日時に修正する
touch -t [[CC]YY]MMDDhhmm[.SS] ファイル名
```

実際にやってみます。

```
# 2020年2月28日10時10分にする
$ touch -t 202002281010 afile

# タイムスタンプを見る
$ ls -l afile
-rw-r--r--  1 mass-min  staff  0  2 28  2020 afile
```

おやっ、変更はできているようですが、時刻の表記がなくなって日付だけになってしまいました。 ~~どうやら`t`オプションで時刻指定をすると、年月日だけの表記になるっぽいですね。~~
@angel_p_57 さんから[コメント](https://qiita.com/mass-min/items/89d69378f8c91e4895c5#comment-68ba1ca501ccdeb234e7)をいただいた通り、 
> lsがタイムスタンプの出力形式を、「ここ最近の日付かどうか」で判断して使い分けているから

最近の日付でないものはタイムスタンプの形式が年月日だけの表記になるようです。この `最近` というのがミソで、Macでは `日付が6ヶ月以上前、または1分でも未来`だと最近の日付ではないと判断されるようです。

```:過去の日付を入れたときのタイムスタンプの表示
$ date
2020年 2月29日 土曜日 12時12分04秒 JST
$ ls -l afile
-rw-r--r--  1 mass-min  staff  0  2 29 12:12 afile
$ touch -t 201908311212 afile
$ ls -l afile
-rw-r--r--  1 mass-min  staff  0  8 31  2019 afile
$ touch -t 201908311213 afile
$ ls -l afile
-rw-r--r--  1 mass-min  staff  0  8 31 12:13 afile
```

```:未来の日付を入れたときのタイムスタンプの表示
$ date
2020年 2月29日 土曜日 11時56分00秒 JST
$ ls -l afile
-rw-r--r--  1 mass-min  staff    0  2 29 11:56 afile
$ touch -t 202002291157 afile
$ ls -l afile
-rw-r--r--  1 mass-min  staff    0  2 29  2020 afile
```

本題に戻ります！`ls`コマンドには完全な時刻表記をする`T`オプションがありますので、それを使って再度確認してみましょう。


```
$ ls -lT afile
-rw-r--r--  1 mass-min  staff  0  2 28 10:10:00 2020 afile
```

正しく変更できていることが確認できました！:tada:

#### 他のファイルと同じタイムスタンプにする
他のファイルと更新日時を揃えたいときの使い方を見ましょう。`r`オプションで参照(reference)するファイルを指定します。

```:touchで更新日時を他のファイルの日時に揃える
touch -r 参照するファイル名 変更対象のファイル名
```

実際にやってみます。

```
$ ls -l
total 0
-rw-r--r--  1 mass-min  staff  0  2 28  2020 afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 bfile
$ touch -r bfile afile
$ ls -l
total 0
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 bfile
```

afileの更新日時がbfileの日時に合わせられました！🥳
納品前にファイルのタイムスタンプを合わせておくと、いつのタイミングの納品で変更があったのか判別する材料にもなりますね(まぁこういうのはGit管理するのが一番ですけどね😅)

#### 空のファイルを作成する

touchコマンドは、指定したファイルが存在しなかったらその名前のファイルを作成してくれます。
空のファイルを先に作っておきたいときに使えますね :thumbsup: 
実際に作ってみましょう。

```
$ ls -l
total 0
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 bfile
$ touch cfile
$ ls -l
total 0
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:18 bfile
-rw-r--r--  1 mass-min  staff  0  2 28 00:45 cfile
```

touchコマンドで、新たにcfileを作成することが出来ました！🥳

### 現在の日時の表示・設定( date コマンド)
既にちょっと出てきていますが、dateコマンドは日時の表示、設定が出来るコマンドです。
システムが持っている時計機能の取得・設定を行うってことですね。

#### 現在時刻の取得

dateとそのまま打つと、現在時刻が取得できます。

```:現在時刻の取得
$ date
2020年 2月28日 金曜日 00時50分33秒 JST
```

#### 現在時刻を設定
dateの引数にタイムスタンプ形式で時刻を渡すことで、その時刻を現在時刻として設定できます。
試しに現在時刻を2020年2月28日1時2分にしてみましょう。

```:日時の設定
$ date 022801022020
date: settimeofday (timeval): Operation not permitted
$ sudo date 022801022020
Password:
2020年 2月28日 金曜日 01時02分00秒 JST
```

日時情報の設定は、スーパーユーザーだけに許可されているんですね。まぁそうですよね、時間がコロコロ変えられちゃったら困りますからね😅

ちなみにこうやって手動で時刻を変更しても、システムがインターネット経由で時刻を合わせる設定になっている場合はすぐに修正されます。

Macだと、`システム環境設定 > 日付と時刻` で `日付と時刻を自動的に設定`にチェックが入っている場合はインターネット経由で自動的に時刻合わせがされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155576/e5fdfd7e-66ba-e701-ca65-fc5e0f2eae2a.png)

試しにこのチェックをはずして、コマンドで打った時刻が設定され続けるのを確認してみましょう。日付を2/27に変えてみます。

```
$ sudo date 022701022020
sudo: ignoring time stamp from the future
Password:
2020年 2月27日 木曜日 01時02分00秒 JST
```

結果は...？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155576/d1af03cf-cdc1-264b-b9c7-7e6df09fbf38.png)

ちゃんと変更できてますね！
とはいえ基本的には、時刻合わせはntpdとかで自動的に行うのが良いです。さっきも書きましたが、サーバー内の時間設定をコロコロ変えられたら溜まったもんじゃないですからね😂

ディレクトリ操作
---
### ディレクトリ内容の表示( ls コマンド)
もういまさら言うまでもなく、めちゃくちゃ使う`ls`コマンド。ディレクトリの内容を表示するのに使います。

```
ls [オプション] [パス名]
```

パス指定の仕方は[第2回の特殊なディレクトリ記号](https://qiita.com/onukid007/items/149dd210bd96cd43bb21#%E7%89%B9%E6%AE%8A%E3%81%AA%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E8%A8%98%E5%8F%B7)でも触れましたね。ここでは、実践で使える使い方をピックアップします。

#### 更新時刻の新しい順に表示
`t`オプションで、最近更新された順に並び替えて表示します。ファイル名とともに更新時刻もフルで表示したいので、さっき使った`l`オプション、`T`オプションと合わせて表示してみます。

```:更新時刻の新しい順に表示
$ ls -tlT
total 0
-rw-r--r--  1 mass-min  staff  0  2 28 00:45:58 2020 cfile
-rw-r--r--  1 mass-min  staff  0  2 27 23:18:10 2020 afile
-rw-r--r--  1 mass-min  staff  0  2 27 23:18:10 2020 bfile
```

ちゃんと新しい順に並んでますね！ :ok_hand: 

#### サイズを見やすく表示
以下のように表示されたファイル群、サイズがどのくらいなのかがスッと入ってきづらいですね:thinking: 

```
$ ls -l ~/Desktop/
total 32
drwxr-xr-x  26 mass-min  staff    832  2 10  2019 Videos
drwxr-xr-x  11 mass-min  staff    352  1 18 17:42 asahi_images
-rw-r--r--@  1 mass-min  staff  14793  2 27 18:07 スクリーンショット 2020-02-27 18.07.45.png
```

そんなときは、 `h`オプションで人間にとって読みやすい(human readable)表記にしましょう！

```
$ ls -lh ~/Desktop/
total 32
drwxr-xr-x  26 mass-min  staff   832B  2 10  2019 Videos
drwxr-xr-x  11 mass-min  staff   352B  1 18 17:42 asahi_images
-rw-r--r--@  1 mass-min  staff    14K  2 27 18:07 スクリーンショット 2020-02-27 18.07.45.png
```

おおっ！理解しやすくなりましたね！🥳バイト単位の表記だと数字が大きすぎてサイズ感がつかめなかったファイルも、こうすればスッと入ってきます！

#### サブディレクトリの内容も表示
lsコマンドでは、 指定したディレクトリパスだけではなくサブディレクトリの内容も確認できる `R` オプションが用意されています。親階層を指定して `ls -R`とすれば、いっきに中身が確認できますね！実際にどう表示されるのか確認してみましょう。

```
$ mkdir Dir1
$ mkdir Dir1/Dir2
$ touch Dir1/aafile
$ touch Dir1/Dir2/aaafile
$ touch Dir1/Dir2/bbbfile
$ ls -R
Dir1	afile	bfile	cfile

./Dir1:
Dir2	aafile

./Dir1/Dir2:
aaafile	bbbfile
```
おお！孫階層までもちゃんと表示されていますね！:tada:

#### 色をつけて表示
`G` オプションで、ファイルタイプごとに色分けして表示をしてくれます。 ファイルタイプを記号として付加してくれる`F`オプションと合わせて、Laravelアプリのルートを表示してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155576/de664453-e84f-af59-eda2-bafd779e3bc8.png)

色がついてる！これが`G`オプションのパワー！それから、ディレクトリには名前の後ろに `/` がついてますね。こちらは `F` オプションのパワーです！💪

### ディレクトリの作成( mkdir コマンド)
これもまたまた先に出てきてしまってますが、ディレクトリの作成は `mkdir` コマンドで行います。

```
mkdir [オプション] ディレクトリ名s
```

これもみんな結構な頻度使ってるかな？便利な使い方だけピックアップします💪

#### 複数ディレクトリを一度に作成
`mkdir [オプション] ディレクトリ名s` のsってなんだよ！って思った人。気になるよね。実は `mkdir` では、複数のディレクトリをいっぺんに作ることができちゃいます。こんな感じ。

```
$ ls
Dir1	afile	bfile	cfile
$ mkdir Dir2 Dir3
$ ls
Dir1	Dir2	Dir3	afile	bfile	cfile
```

2ついっぺんに作ることができちゃいました！複数指定すれば、指定した分だけディレクトリを作ってくれます。

#### 子ディレクトリの同時作成
mkdirは、ディレクトリパスを指定したときに親のディレクトリがないとエラーになります。

```
$ ls
Dir1	Dir2	Dir3	afile	bfile	cfile

# Dir3は存在するので、Dir3/Dir3_1は作成できる
$ mkdir Dir3/Dir3_1

# Dir4は存在しないので、Dir4/Dir4_1は作成できない
$ mkdir Dir4/Dir4_1
mkdir: Dir4: No such file or directory
```

いちいち `Dir4を作る` -> `Dir4/Dir4_1を作る` としてもよいのですが、面倒なので一気に作っちゃおうっていうのがここで紹介する `p`オプションです。ディレクトリパスのうち、存在しないディレクトリを作成しつつ最終的に対象のディレクトリを作成します。

```
$ ls
Dir1	Dir2	Dir3	afile	bfile	cfile
$ mkdir -p Dir4/Dir4_1
$ ls -R
Dir1	Dir2	Dir3	Dir4	afile	bfile	cfile

./Dir1:
Dir2	aafile

./Dir1/Dir2:
aaafile	bbbfile

./Dir2:

./Dir3:
Dir3_1

./Dir3/Dir3_1:

./Dir4:
Dir4_1

./Dir4/Dir4_1:
```

`Dir4/Dir4_1`が一気に作成できていますね！🥳

#### 権限設定しながらディレクトリ作成
作成したディレクトリに対して権限の変更をかけるには `chmod` コマンドを実行します。
でも実は、 `mkdir`コマンドでディレクトリを作成するときに `m` オプションで指定すれば予め権限設定をした状態でディレクトリが作れるんですよ。知ってました？自分は知りませんでした🤪

```
$ ls -la
total 0
drwxr-xr-x   9 mass-min  staff  288  2 28 02:09 .
drwxr-xr-x  13 mass-min  staff  416  2 27 23:13 ..
drwxr-xr-x   4 mass-min  staff  128  2 28 01:33 Dir1
drwxr-xr-x   2 mass-min  staff   64  2 28 01:59 Dir2
drwxr-xr-x   3 mass-min  staff   96  2 28 02:02 Dir3
drwxr-xr-x   3 mass-min  staff   96  2 28 02:09 Dir4
$ chmod 400 Dir4
$ ls -la
total 0
drwxr-xr-x   6 mass-min  staff  192  2 28 02:13 .
drwxr-xr-x  13 mass-min  staff  416  2 27 23:13 ..
drwxr-xr-x   4 mass-min  staff  128  2 28 01:33 Dir1
drwxr-xr-x   2 mass-min  staff   64  2 28 01:59 Dir2
drwxr-xr-x   3 mass-min  staff   96  2 28 02:02 Dir3
dr--------   3 mass-min  staff   96  2 28 02:09 Dir4
```

権限設定しつつディレクトリを作成してみます。

```
$ mkdir -m 400 Dir5
$ ls -l
total 0
drwxr-xr-x  4 mass-min  staff  128  2 28 01:33 Dir1
drwxr-xr-x  2 mass-min  staff   64  2 28 01:59 Dir2
drwxr-xr-x  3 mass-min  staff   96  2 28 02:02 Dir3
dr--------  3 mass-min  staff   96  2 28 02:09 Dir4
dr--------  2 mass-min  staff   64  2 28 02:15 Dir5
```

ちゃんと権限設定できてますね！地味に便利！:joy:

### ディレクトリの削除( rmdir コマンド)
ディレクトリを削除するときに使うのが `rmdir` コマンド...なのですが、自分は正直ほとんど使ってません。
というのも、このコマンドが消せるのは`空のディレクトリのみ`と超限定的で、使いみちがほとんど思いつかないからです。 `rm -r` で代用できちゃうよね。タイプ数も変わらないし。というわけで実例だけ載せてさらっといきます。

```
$ ls -l
total 0
drwxr-xr-x  4 mass-min  staff  128  2 28 01:33 Dir1
drwxr-xr-x  2 mass-min  staff   64  2 28 01:59 Dir2
drwxr-xr-x  3 mass-min  staff   96  2 28 02:02 Dir3
dr--------  3 mass-min  staff   96  2 28 02:09 Dir4
dr--------  2 mass-min  staff   64  2 28 02:15 Dir5
$ rmdir Dir5
$ ls -la
total 0
drwxr-xr-x   6 mass-min  staff  192  2 28 02:20 .
drwxr-xr-x  13 mass-min  staff  416  2 27 23:13 ..
drwxr-xr-x   4 mass-min  staff  128  2 28 01:33 Dir1
drwxr-xr-x   2 mass-min  staff   64  2 28 01:59 Dir2
drwxr-xr-x   3 mass-min  staff   96  2 28 02:02 Dir3
dr--------   3 mass-min  staff   96  2 28 02:09 Dir4
```

はい、削除できました。ちなみに `中身がら空でないディレクトリ`に対して `rmdir` コマンドを実行するとこうなります。

```
$ rmdir Dir1
rmdir: Dir1: Directory not empty
```

そっすねって感じです🥳

### ディレクトリの移動と名前変更( mv コマンド)
ファイルの移動にも `mv` コマンドを使いますが、ディレクトリも同じく `mv` コマンドで移動させることが出来ます。

#### ディレクトリの移動
[第2回](https://qiita.com/onukid007/items/149dd210bd96cd43bb21#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%A7%BB%E5%8B%95%E3%81%A8%E5%90%8D%E5%89%8D%E5%A4%89%E6%9B%B4%E3%82%92%E3%81%99%E3%82%8B)で学んだ通りですね！特に新しいことはありません。

```:ディレクトリの移動
mv ディレクトリ名1 ディレクトリ名2
```

実際にやってみます。

```
$ ls
Dir1	Dir2	Dir3
$ mv Dir2 Dir3
$ ls -R
Dir1	Dir3

./Dir1:
Dir2	aafile

./Dir1/Dir2:
aaafile	bbbfile

./Dir3:
Dir2	Dir3_1

./Dir3/Dir2:

./Dir3/Dir3_1:
```

Dir2をDir3の中に移動させることが出来ました！:tada:

#### ディレクトリ名の変更
これもそのままですね。ディレクトリ名2が存在しなければ、ディレクトリ名1がディレクトリ名2となります。

```:ディレクトリ名の変更
mv ディレクトリ名1 ディレクトリ名2
```

実際に変更してみます。

```
$ ls
Dir1	Dir3
$ mv Dir1 Dir11
$ ls
Dir11	Dir3
```

Dir1がDir11に変わりましたね！🥳ディレクトリの中身は引き継がれ、ディレクトリ名のみ変わっています。

まとめ
---
本当に基礎的な部分ですし、よく使うコマンドばかりだったので、つまづくところは殆どなかったかと思います。こういう基礎コマンドを使うときこそ、 `man` コマンドで引数のとり方やオプション見て便利な使い方を探る練習をしましょう。ググり方を覚えるのと同じで、マニュアル見るのに慣れておくと作業が数段捗ります。それに、あまり知られていない意外な使い方が隠れているかもしれませんよ〜😆ステキな使い方を見つけたら、是非教えてくださいね🤗

では今回はこの辺にて！また次回の勉強会記事でお会いしましょう！👋

## 過去の勉強会の概要とリンクはこちら↓
- [Linux勉強会 第1回：仮想コンソールとファイル操作](https://qiita.com/yungo/items/8f9cb967f0bd5fa35242)
  - 仮想コンソール
     - 仮想コンソールで何ができるのか
     - ttyを指定して出力
  - ファイル操作
     - ファイルの簡易作成方法
     - ファイル名の指定方法
- [Linux勉強会 第2回：ファイル操作](https://qiita.com/onukid007/items/149dd210bd96cd43bb21)
  - 特殊なディレクトリ記号について
  - ファイル内容を表示する
  - ファイルの移動と名前変更をする
- [Linux勉強会 第3回：ファイルの削除禁止設定と検索機能](https://qiita.com/kei1111/items/c43d0c8b41e1f76d7a0d)
  - ファイルの削除禁止設定
  - ファイルを探す
  - その他の検索機能