如何在 Linux 上遍歷目錄樹
已發表: 2022-07-21
Linux 上的目錄允許您將文件分組到不同的單獨集合中。 缺點是從一個目錄移動到另一個目錄來執行重複性任務變得乏味。 這是自動化的方法。
所有關於目錄
當您被介紹到 Linux 時,您學習的第一個命令可能是ls
,但cd
也不會落後太多。 了解目錄以及如何在它們周圍移動,尤其是嵌套的子目錄,是了解 Linux 如何組織自身以及如何將自己的工作組織到文件、目錄和子目錄中的基本部分。
掌握目錄樹的概念——以及如何在它們之間移動——是您在熟悉 Linux 環境時所經歷的許多小里程碑之一。 使用帶有路徑的cd
會將您帶到該目錄。 像cd ~
或cd
這樣的快捷方式可以將您帶回您的主目錄,然後cd ..
將您在目錄樹中上移一級。 簡單的。
但是,沒有一種同樣簡單的方法可以在目錄樹的所有目錄中運行命令。 我們可以通過不同的方式實現該功能,但沒有專門用於該目的的標準 Linux 命令。
某些命令,例如ls
,具有強制它們以遞歸方式操作的命令行選項,這意味著它們從一個目錄開始,並有條不紊地遍歷該目錄下的整個目錄樹。 對於ls
,它是-R
(遞歸)選項。
如果您需要使用不支持遞歸的命令,則必須自己提供遞歸功能。 這是如何做到這一點的。
相關:你應該知道的 37 個重要的 Linux 命令
樹命令
tree
命令不會幫助我們完成手頭的任務,但它確實可以很容易地查看目錄樹的結構。 它在終端窗口中繪製樹,以便我們可以即時了解構成目錄樹的目錄和子目錄,以及它們在樹中的相對位置。
您需要安裝tree
。
在 Ubuntu 上,您需要輸入:
sudo apt 安裝樹
在 Fedora 上,使用:
sudo dnf 安裝樹
在 Manjaro 上,命令是:
sudo pacman -Sy 樹
使用不帶參數的tree
會在當前目錄下繪製樹。
樹
您可以在命令行上傳遞tree
的路徑。
樹工作
-d
(目錄)選項排除文件,只顯示目錄。
樹 -d 工作
這是獲得目錄樹結構清晰視圖的最方便的方法。 此處顯示的目錄樹是以下示例中使用的目錄樹。 有五個文本文件和八個目錄。
不要將 ls 的輸出解析到遍歷目錄
您的第一個想法可能是,如果ls
可以遞歸地遍歷目錄樹,為什麼不使用ls
來執行此操作並將輸出傳遞到解析目錄並執行某些操作的其他一些命令中呢?
解析ls
的輸出被認為是不好的做法。 由於 Linux 能夠創建包含各種奇怪字符的文件和目錄名稱,因此創建通用的、普遍正確的解析器變得非常困難。
您可能永遠不會故意創建像這樣荒謬的目錄名稱,但腳本或應用程序中的錯誤可能會。
解析合法但考慮不周的文件和目錄名稱很容易出錯。 我們可以使用其他方法,它們比依賴解釋ls
的輸出更安全、更健壯。
使用查找命令
find
命令具有內置的遞歸功能,它還具有為我們運行命令的能力。 這使我們能夠構建強大的單線。 如果它是您將來可能想要使用的東西,您可以將您的單行代碼變成別名或 shell 函數。
該命令遞歸地遍歷目錄樹,查找目錄。 每次找到目錄時,它都會打印出目錄的名稱並在該目錄中重複搜索。 完成對一個目錄的搜索後,它會退出該目錄並在其父目錄中繼續搜索。
找工作 -type d -execdir echo "In:" {} \;
您可以通過列出目錄的順序來查看搜索在樹中的進展情況。 通過比較tree
命令的輸出與find
one-liner 的輸出,您將看到find
如何依次搜索每個目錄和子目錄,直到找到沒有子目錄的目錄。 然後它返回一個級別並在該級別恢復搜索。
以下是命令的組成方式。
- find :
find
命令。 - work :開始搜索的目錄。這可以是路徑。
- -type d :我們正在尋找目錄。
- -execdir :我們將在找到的每個目錄中執行一個命令。
- echo “In:” {} :這是命令。我們只是將目錄的名稱回顯到終端窗口。 “{}”保存當前目錄的名稱。
- \; : 這是一個用於終止命令的分號。 我們需要用反斜杠轉義它,這樣 Bash 就不會直接解釋它。
稍作改動,我們可以讓 find 命令返回匹配搜索線索的文件。 我們需要包含 -name 選項和搜索線索。 在此示例中,我們正在尋找與“*.txt”匹配的文本文件,並將其名稱回顯到終端窗口。

查找工作 -name "*.txt" -type f -execdir echo "Found:" {} \;
搜索文件還是目錄取決於您要實現的目標。 要在每個目錄中運行命令,請使用-type d
。 要對每個匹配的文件運行命令,請使用-type f
。
此命令計算起始目錄和子目錄中所有文本文件中的行數。
查找工作 -name "*.txt" -type f -execdir wc -l {} \;
相關:如何在 Linux 中使用 find 命令
使用腳本遍歷目錄樹
如果您需要遍歷腳本中的目錄,您可以在腳本中使用find
命令。 如果您需要(或只是想)自己進行遞歸搜索,您也可以這樣做。
#!/bin/bash shopt -s dotglob nullglob 函數遞歸{ 本地 current_dir dir_or_file 對於 $1 中的 current_dir; 做 echo "目錄命令:" $current_dir 對於 "$current_dir"/* 中的 dir_or_file; 做 如果 [[ -d $dir_or_file ]]; 然後 遞歸“$dir_or_file” 別的 wc $dir_or_file 菲 完畢 完畢 } 遞歸“$ 1”
將文本複製到編輯器中並將其保存為“recurse.sh”,然後使用chmod
命令使其可執行。
chmod +x 遞歸.sh
該腳本設置了兩個 shell 選項, dotglob
和nullglob
。
dotglob
設置表示以句點“ .
” 將在擴展通配符搜索詞時返回。 這實際上意味著我們在搜索結果中包含隱藏文件和目錄。
nullglob
設置意味著未找到任何結果的搜索模式將被視為空字符串或空字符串。 他們不默認搜索詞本身。 換句話說,如果我們使用星號通配符“ *
”搜索目錄中的所有內容,但沒有結果,我們將收到空字符串而不是包含星號的字符串。 這可以防止腳本無意中嘗試打開名為“*”的目錄,或將“*”視為文件名。
接下來,它定義了一個名為recursive
的函數。 這就是有趣的事情發生的地方。
聲明了兩個變量,稱為current_dir
和dir_or_file
。 這些是局部變量,只能在函數內引用。
函數中還使用了一個名為$1
的變量。 這是調用函數時傳遞給函數的第一個(也是唯一一個)參數。
該腳本使用兩個for
循環,一個嵌套在另一個內部。 第一個(外部) for
循環用於兩件事。
一種是運行您想要在每個目錄中執行的任何命令。 我們在這裡所做的只是將目錄的名稱回顯到終端窗口。 您當然可以使用任何命令或命令序列,或者調用另一個腳本函數。
外部 for 循環所做的第二件事是檢查它可以找到的所有文件系統對象——可以是文件也可以是目錄。 這就是內部for
循環的目的。 反過來,每個文件或目錄名稱都被傳遞到dir_or_file
變量中。
然後在 if 語句中測試dir_or_file
變量以查看它是否是目錄。
- 如果是,則函數調用自身並將目錄名稱作為參數傳遞。
- 如果
dir_or_file
變量不是目錄,那麼它必須是文件。 您希望應用於文件的任何命令都可以從if
語句的else
子句中調用。 您還可以在同一腳本中調用另一個函數。
腳本中的最後一行調用recursive
函數並傳入第一個命令行參數$1
作為要搜索的起始目錄。這就是整個過程的開始。
讓我們運行腳本。
./recurse.sh 工作
遍歷目錄,腳本中將在每個目錄中運行命令的點由“Directory command for:”行指示。 找到的文件會在其上運行wc
命令以計算行數、單詞和字符數。
處理的第一個目錄是“work”,然後是樹的每個嵌套目錄分支。
需要注意的一個有趣的點是,您可以通過將特定於目錄的命令從內部 for 循環的上方移動到其下方來更改目錄的處理順序。
讓我們將“Directory command for:”行移到內部for
循環done
之後。
#!/bin/bash shopt -s dotglob nullglob 函數遞歸{ 本地 current_dir dir_or_file 對於 $1 中的 current_dir; 做 對於 "$current_dir"/* 中的 dir_or_file; 做 如果 [[ -d $dir_or_file ]]; 然後 遞歸“$dir_or_file” 別的 wc $dir_or_file 菲 完畢 echo "目錄命令:" $current_dir 完畢 } 遞歸“$ 1”
現在我們將再次運行該腳本。
./recurse.sh 工作
這一次,這些目錄首先從最深的層次應用到它們的命令,備份樹的分支。 最後處理作為參數傳遞給腳本的目錄。
如果首先處理更深的目錄很重要,那麼您可以這樣做。
遞歸很奇怪
這就像用自己的電話給自己打電話,給自己留言,告訴自己下次見面的時候——重複。
在您掌握它的好處之前可能需要付出一些努力,但是當您這樣做時,您會發現它是一種解決難題的優雅的編程方式。
相關:什麼是編程中的遞歸,以及如何使用它?