# Linux (四) - 檔案與目錄操作
在 [Linux (二) - 檔案系統架構](https://tienyulin.github.io/linux-filesystem/) 中,我們有介紹過了幾個用來操作檔案和目錄的基本指令,`ls`、`cd`、`mkdir`。這一篇要來介紹更多常用的檔案和目錄操作指令。
<!-- more -->
## 路徑
操作檔案必須要先知道檔案的路徑才能進行存取,而存取檔案的路徑可以分為絕對路徑和相對路徑。
### 絕對路徑
絕對路徑就是直接指定路徑,例如 : `/home/user1/test.txt`。
### 相對路徑
相對路徑通常是用於退回到前幾層再去訪問其他的目錄,退回前一層的方式是加上 `../`。所以相對路徑看起來就會像是 `../../user1/test.txt`。
如果是要指定當前路徑則是 `.`。
### 切換路徑
有了路徑之後就可以透過 `cd` 指令切換路徑,例如 : `cd /home/user1` 就可以切換到 `user1` 目錄下。
更多切換路徑的範例請參考 [Linux (二) - 檔案系統架構](https://tienyulin.github.io/linux-filesystem/#cd)。
## 常用命令
以下介紹一些常用的命令,由於 [Linux (二) - 檔案系統架構](https://tienyulin.github.io/linux-filesystem/#cd) 已經介紹過 `ls`、`cd`、`mkdir` 了,這裡就不在介紹,如果你還不了解這些指令建議先去看一下。
### pwd
pwd (print work directory) 用於顯示目前所在的目錄名稱。
```bash=
$ pwd [-P]
```
`-P` 是可以選擇的參數,如果有加會列出真實的路徑,所以如果是連結檔(捷徑),就不會列出連結檔路徑而是檔案原本的位置。
**範例**
```bash=
$ pwd
/home/testDir
```
### rmdir
rmdir (remove directory) 用於刪除空的目錄。
```bash=
$ rmdir [-p]
```
`-p` 是可以選擇的參數,如果有加可以直接刪除上一層空的目錄。
**範例**
首先先建立兩層的目錄,接者用 `rmdir` 指定刪除第二層 `testDir`,完成後列出當前目錄會發現第一層目錄 `testDir1` 還在。所以如果希望連第一層都刪掉的話就必須加上 `-p` 參數,執行完後會發先第一層目錄 `testDir1` 已經連同第二層一起被刪掉了。
```bash=
$ mkdir testDir1
$ mkdir testDir1/testDir2
$ rmdir testDir1/testDir2
$ ls
testDir1
$ mkdir testDir1/testDir2
$ rmdir -p testDir1/testDir2
$ ls
```
### cp
cp (copy) 用於複製檔案或目錄,而預設複製的檔案的權限會將擁有者設定給操作指令的使用者。
```bash=
cp [option] source|[source] [destination]
```
option :
* `-a` : 會完整複製檔案,包含權限設定,如果是複製目錄則會複製目錄下所有檔案,等同 `-pdr` 三個功能。
* `-d` : 若來源檔是連結檔,則會複製連結檔屬性而不是文件本身。
* `-f` : 強制 (force) 複製,如果目標文件已經存在且無法開啟則會移除後再試一次。
* `-i` : 若目標檔已經存在則覆蓋前會先詢問。
* `-l` : 建立實體連結檔到目標檔案。
* `-p` : 連同文件屬性一起複製,非使用預設的屬性,常用於備份。
* `-r` : 遞迴持續複製,也就是會將整個目錄下的所有檔案都複製。
* `-s` : 複製成為符號連結檔 (symbolic link),就是捷徑。
* `-u` : 目標比來源舊才更新,或是目標檔不存在才複製。
**範例**
下面是一個最簡單的範例,單純將 `test1.txt` 複製到 `testDir1` 目錄下。
```bash=
$ cp test1.txt testDir1
$ ls testDir1
test1.txt
```
下面這個範例先將 `test1.txt` 的檔案擁有者換成 `user1`,而當前的使用者是 `user2`,可以看到複製完後檔案的擁有者變成了 `user2`。
```bash=
user2@myComputer$ chown user1 test1.txt
user2@myComputer$ ls -l test1.txt
-rw-r--r-- 1 user1 group1 0 Sep 30 06:33 test1.txt
user2@myComputer$ cp test1.txt testDir1
user2@myComputer$ ls -l testDir1
total 0
-rw-r--r-- 1 user2 group1 0 Sep 30 07:25 test1.txt
```
接著我們加入 `-a` 和 `-f` 參數,`-a` 是為了測試加了 `-a` 之後權限是否不會被改掉,而 `-f` 則是不需要刪掉先前的檔案,可以強制覆蓋過去。可以看到最後檔案的擁有者還是維持 `user`。
```bash=
user2@myComputer$ chown user1 test1.txt
user2@myComputer$ ls -l test1.txt
-rw-r--r-- 1 user1 group1 0 Sep 30 06:33 test1.txt
user2@myComputer$ cp -af test1.txt testDir1
user2@myComputer$ ls -l testDir1
total 0
-rw-r--r-- 1 user1 group1 0 Sep 30 07:25 test1.txt
```
下面這個範例加入 `-i` 參數,可以看到因為檔案已經存在了,所以會先詢問是否要覆寫。
```bash=
$ cp -ai test1.txt testDir1
cp: overwrite 'testDir1/test1.txt'?
```
接下來在來實測 `-a` 是否真的會複製目錄下所有檔案,這裡要先自行將要被複製的目錄新增多層目錄及添加檔案。可以看到原本 `testDir3` 是沒有檔案的,複製完後目錄下多了不只一層且含有檔案。
```bash=
$ mkdir testDir3
$ ls -l testDir3
total 0
$ cp -a testDir1 testDir3
$ ls -l testDir3
total 4
drwxr-xr-x 4 root root 4096 Sep 30 09:28 testDir1
$ ls -l testDir3/testDir1
total 8
-rw-r--r-- 1 tony root 0 Sep 30 06:33 test1.txt
-rw-r--r-- 1 root root 0 Sep 30 09:28 test2.txt
drwxr-xr-x 2 root root 4096 Sep 30 09:28 testDir2
drwxr-xr-x 2 root root 4096 Sep 30 09:28 testDir3
```
另外像是 `-p` 和 `-r` 其實也是可以透過組合成 `-pr` 變成 `-a`,所以如果是要完整複製一模一樣的檔案或是目錄可以直接使用 `-a` 就好。
### rm
rm (remove) 用於刪除檔案或目錄。
```bash=
rm [option] [file/folder]
```
option :
* -f : 強制 (force) 刪除,不會出現警告訊息。
* -i : 互動 (interactive) 模式,刪除前會先詢問使用者。
* -r : 遞迴 (recursive) 刪除,用於刪除目錄,但也非常危險。
**範例**
首先先刪除檔案,可以看到 `-i` 其實不一定要加,預設就會在刪除前詢問是否要刪除。
```bash=
$ rm -i test1.txt
rm: remove regular empty file 'test1.txt'? y
```
接著使用 `-f` 強制刪除可以發現並不會詢問是否要刪除。
```bash=
rm -f test3.txt
```
最後來測試刪除資料夾,可以發現直接刪除是會失敗的,所以如果是刪除資料夾一定要加 `-r`。
```bash=
$ rm -i testDir1
rm: cannot remove 'testDir1': Is a directory
$ rm -r testDir1
rm: descend into directory 'testDir1'? y
rm: descend into directory 'testDir1/testDir2'? y
rm: remove regular empty file 'testDir1/testDir2/test1.txt'? y
rm: remove directory 'testDir1/testDir2'? y
rm: remove regular empty file 'testDir1/test2.txt'? y
rm: descend into directory 'testDir1/testDir3'? y
rm: remove regular empty file 'testDir1/testDir3/test3.txt'? y
rm: remove directory 'testDir1/testDir3'? y
rm: remove regular empty file 'testDir1/test1.txt'? y
rm: remove directory 'testDir1'? y
```
### mv
mv (move) 用於移動檔案或目錄,也可以用來修改名稱。
```bash=
mv [option] [source] [destination]
mv [option] source|[source] [directory]
```
option :
* -f : 強制 (force) 覆蓋,如果目標 (destination) 檔案已經存在會直接覆蓋不會詢問。
* -i : 互動 (interactive) 模式,如果目標檔案已經存在,會先詢問是否覆蓋。
* -u : 更新 (update) 檔案,如果目標檔案已經存在且來源 (source) 檔案比較新才會更新。
**範例**
首先先建立一些測試用的檔案和資料夾,這個範例我們先測試使用 `-f` 參數,可以看到在不經詢問下 `testmv` 最後順利移動到了 `mvDirTest1` 資料夾中。
```bash=
$ touch testmv testmv2
$ ls
testmv testmv2
$ mkdir mvDirTest1 mvDirTest2
$ ls
mvDirTest1 mvDirTest2 testmv testmv2
$ mv -f testmv mvDirTest1
$ ls mvDirTest1
testmv
```
接著為了測試 `-i` 參數的詢問是否覆蓋已經存在的檔案,再次新建立一個 `testmv`,並移動到 `mvDirTest1`,可以看到會在移動前詢問是否覆蓋已經存在的 `testmv`。
```bash=
$ touch testmv
$ mv -i testmv mvDirTest1
mv: overwrite 'mvDirTest1/testmv'? y
```
除了移動檔案到資料夾下,也可以移動資料夾到另一個資料夾中,如下 :
```bash=
$ mv mvDirTest1 mvDirTest2
$ ls mvDirTest2
mvDirTest1
```
mv 指令也可以重新命名檔案,概念其實就是將現有檔案移到一個新的檔案就算是重新命名,如下 :
```bash=
$ mv testmv2 testmv21
$ ls
mvDirTest1 mvDirTest2 testmv21
```
## 進階檔案操作命令
前面介紹了一些常用的檔案基本命令,下面要來介紹幾個比較進階的命令。
### touch
touch 指令可以用於新增空白檔案也可以用來更改檔案的時間戳記。但是較常用的就是用來建立空白檔案,所以更改檔案時間戳記的做法請參考 [Linux Touch命令的8種常見用法](https://ubuntuqa.com/zh-tw/article/7760.html)。
```bash=
touch file|[file]
```
**範例**
touch 指令建立檔案可以一次建立多個,在先前的一些範例也都有出現過,只要指定好檔名即可。
```bash=
$ touch t1.txt t2
$ ls
t1.txt t2
```
### cat
cat (concatenate) 指令可以用於`顯示檔案內容`、`建立檔案`和`合併檔案內容`。
#### 顯示檔案內容
```bash=
cat [option]
```
option :
* -A : 等於 `-vET`,可以列出一些特殊字元而不是空白而已。
* -b : 列出行號,空白行不標行號。
* -E : 將結尾的段行 `$` 顯示出來
* -n : 列出行號,包含空白行也有行號。
* -T : 將 `tab` 以 `^I` 顯示。
* -v : 列出一些看不出的特殊字元。
**範例**
加上 `-b` 參數,可以看到只有非空行會顯示行數。
```bash=
$ cat -b testcat
1 This is a test.
2 this is a test.
3 This is a tab test.
```
加上 `-n` 參數,可以看到每一行都會顯示行數。
```bash=
$ cat -n testcat
1 This is a test.
2
3 this is a test.
4
5 This is a tab test.
6
```
加上 `-E` 參數,可以看到每一行都會有 `$` 符號代表換行。
```bash=
$ cat -E testcat
This is a test.$
$
this is a test.$
$
This is a tab test.$
$
```
加上 `-T` 參數,可以看到 This is a`^I`tab`^I`test. 這行被加上了 `^I` 代表那一格是 `tab`。
```bash=
$ cat -T testcat
This is a test.
this is a test.
This is a^Itab^Itest.
```
#### 建立檔案
cat 指令建立檔案和 `touch` 不同的是 cat 在建立時可以直接輸入檔案內容而 touch 只會建立空檔案,需要在建完後再開啟檔案編輯。如果用 `cat >` 對一個已經存在的檔案下命令的話會直接將舊的內容覆蓋掉,這點要特別注意。
```bash=
cat > [file]
```
**範例**
直接建立一個檔案並輸入內容,最後輸入 Ctrl+D 結束輸入。輸入完後可以再用 `cat` 指令列出來看,如下 :
```bash=
$ cat > file1
this is file1.
hello world!
[Ctrl+D]
$ cat file1
this is file1.
hello world!
```
#### 合併檔案內容
cat 指令也可以用於合併多個檔案的內容。
```bash=
cat [file]... > [NewFile]
```
**範例**
下面這個範例可以看到最後新的 `textfile3` 將 `textfile1` 和 `textfile2` 的內容合併在了一起。
```bash=
$ cat textfile1
this is file1
$ cat textfile2
this is file2
$ cat textfile1 textfile2 > textfile3
$ cat textfile3
this is file1
this is file2
```
### 加入內容到檔案後面
前面提到 `cat >` 可以用來建立檔案並且直接輸入內容,而如果是要加資料到檔案的後面可以直接使用 `cat >>` 來加入。
```bash=
cat >> [file]
```
**範例**
下面這個範例可以看到先建立了一個 file.csv 的檔案,並且輸入內容。接著再用 `cat >>` 輸入一筆內容,最後輸出資料就多了一筆新增的內容。
```bash=
$ cat > file.csv
id,name,sex
1,frank,male
2,simon,male
$ cat file.csv
id,name,sex
1,frank,male
2,simon,male
$ cat >> file.csv
3,mary,female
$ cat file.csv
id,name,sex
1,frank,male
2,simon,male
3,mary,female
```
### more
more 指令可以用於檔案內容大於螢幕輸出視窗時,不會一次顯示全部內容導致看不到前面的內容。而是先顯示部分內容,再透過操作繼續顯示後面的內容。
```bash=
more [file]
```
在 more 指令執行的過程中可以使用以下幾個按鍵操作 :
* Enter : 向下翻一行。
* Space : 空白鍵代表向下翻一頁。
* / : 斜線後加上字串可以向下搜尋,例如 : `/net` 是在搜尋 `net` 這個字串。
* b : 往回翻頁。
* q : 立刻離開 more。
**範例**
下面的範例可以看到最後一行的 `--More--(26%)`,這是代表目前顯示的內容量,游標也會在此,所以就可以透過 `Enter`、`Space` 等等的按鍵進行操作。
```bash=
$ more anaconda-post.log
Loaded plugins: fastestmirror, ovl
No Match for argument: dhclient
No Match for argument: dhcp-common
...
---> Package python-firewall.noarch 0:0.6.3-8.el7_8.1 will be erased
--> Running transaction check
--More--(26%)
```
### less
less 指令可以讓檔案輸出時擁有分頁的功能,類似於 `more` 指令。但是 `more` 指令如果要往前翻只能一次翻一頁,而 `less` 指令更有彈性,可以任意的前後翻。此外還可以任意的進行搜索。
```bash=
less [file]
```
在 less 指令執行的過程中可以使用以下幾個按鍵操作 :
* Enter : 向下翻一行。
* Space : 空白鍵代表向下翻一頁。
* / : 斜線後加上字串可以向下搜尋,例如 : `/net` 是在搜尋 `net` 這個字串。
* pagedown : 向上翻一頁。
* pageup : 向下翻一頁。
* q : 立刻離開 more。
**範例**
下面這個範例可以很明顯看到沒有像 `more` 指令一樣最後一行會有 `--More--(26%)`,而且在輸出上會快很多,因為 `more` 是一次載入完才顯示而 `less` 是要看的時候才載入。
```bash=
$ less anaconda-post.log
Loaded plugins: fastestmirror, ovl
No Match for argument: dhclient
No Match for argument: dhcp-common
...
---> Package python-firewall.noarch 0:0.6.3-8.el7_8.1 will be erased
--> Running transaction check :
```
### vi
vi 是一個文字編輯器,經常用於編輯檔案的內容。
```bash=
vi [file]
```
在執行 vi 編輯器時,可以透過以下按鍵進行操作 :
* `I`、`O`、`A` : 代表要輸入 (Insert) 內容,vi 編輯器剛開啟檔案時是預覽模式不能編輯,一定要先按下 `I` 或 `O` 或 `A` 才可以開始編輯。
* `Esc` : 從輸入模式切換回預覽模式,編輯完後必須先切換回預覽模式。
* `:q` : 離開 vi 編輯器。
* `:w` : 儲存變更,將編輯過的內容寫進檔案,可以結合 `:q` 變成 `:wq`,寫入後直接離開。
* `!` : 強制執行,可以搭配 `:w` 或 `:q` 使用。
**範例**
使用 `vi` 編輯器打開檔案後會呈現出檔案的內容,此時是預覽模式不能編輯。
```bash=
$ vi textfile3
this is file1
this is file2
~
~
"textfile3" 2L, 28C
```
要編輯時按下 `I` 或 `O` 或 `A` 就可以開始編輯,並且底部會出現 `-- INSERT --`。
```bash=
this is file1
this is file2
~
~
-- INSERT --
```
編輯完後要先按下 `Esc` 鍵離開編輯模式,並且輸入 `:w` 或 `:q` 等等的命令,輸入的命令會顯示在最後一行。
```bash=
this is file1
this is file2
~
~
:wq
```
## Summary
本篇介紹了一些常用的檔案操作命令,透過這些命令就可以對檔案進行複製、移動、刪除、編輯等等的操作。如此就可以更方便的對這些檔案進行管理和使用了。
## 參考
[1] [Linux 文件與目錄管理](https://www.runoob.com/linux/linux-file-content-manage.html)
[2] [Linux 檔案與目錄管理 | 鳥哥](http://linux.vbird.org/linux_basic/0220filemanager.php)
[3] [Linux touch命令](https://www.runoob.com/linux/linux-comm-touch.html)
[4] [Linux Touch命令的8種常見用法](https://ubuntuqa.com/zh-tw/article/7760.html)
[5] [Linux cat命令用法](https://www.itread01.com/p/204415.html)
[6] [vim 程式編輯器 | 鳥哥](http://linux.vbird.org/linux_basic/0310vi.php)
[7] [linux中的cat,more,less指令使用](https://www.itread01.com/content/1545977108.html)
###### tags: `Linux`