從給定路徑中查詢最後一個目錄或檔案
概述
在處理路徑時,我們經常使用 shell 指令碼或 Linux 命令。提取給定檔名的最後一部分是一項相當常見的任務。
例如,如果我們嘗試訪問 /tmp/dir/target,那麼我們希望能夠訪問 target 作為檔名。
是的,這看起來足夠簡單。但是,可能存在一些邊緣情況會導致我們失敗。
我們將仔細研究這個問題,並探討一些常見的解決方案。
常見解決方案的討論
我們知道 Linux 檔案系統不允許斜槓 (/) 作為檔名或目錄的一部分。
因此,如果我們將輸入路徑字串視為逗號分隔值的列表,那麼我們可以簡單地使用最後一個元素來解決問題。
我們可以使用各種命令來完成我們的任務,包括 grep,它可以幫助我們從文字檔案中過濾出某些行;awk,它允許我們操作文字檔案;等等。
$ sed 's#.*/##' <<< "/tmp/dir/target"
target
$ awk -F'/' '{print $NF}' <<< "/tmp/dir/target"
target
$ grep -o '[^/]*$' <<< "/tmp/dir/target"
target
我們可以使用 Bash 的引數擴充套件來解決問題。
$ INPUT="/tmp/dir/target"
$ echo ${INPUT##*/}
target
可能還有很多其他類似的 CLI 工具,但它們真的足夠穩定以用於生產環境嗎?
如果您使用 /tmp/dir/target/,則以上方法均無效,因為它們假設最後一個字元不是斜槓。
$ sed 's#.*/##' <<< "/tmp/dir/target/"
( empty output )
$ awk -F'/' '{print $NF}' <<< "/tmp/dir/target/"
( empty output )
$ grep -o '[^/]*$' <<< "/tmp/dir/target/"
( empty output )
$ INPUT="/tmp/dir/target/"
$ echo ${INPUT##*/}
( empty output )
我們可能希望修復上述解決方案,以便它們處理斜槓和反斜槓情況。例如,我們可以將 awk 解決方案修改為類似於以下內容:
$ awk -F'/' '{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/tmp/dir/target"
target
$ awk -F'/' '{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/tmp/dir/target/"
target
修復後的 awk 單行命令可用於大多數情況,但仍然存在一些邊緣情況,它可能無法工作。
現在讓我們仔細檢查一下它們。
深入研究極端情況
我們已經看到 Linux 檔案系統可以用一組路徑來表示。現在,我們將檢視這些路徑的一些其他可能模式。
首先,在 Linux 中,/ 是最頂層的目錄。它包含所有其他目錄和檔案。因此,/ 是任何檔案或目錄的有效路徑字串。
此外,大多數 Linux 檔案系統型別允許使用空格作為檔名或目錄名的一部分。因此,如果一個檔案或目錄被稱為“ ”,它也是一個有效的路徑。
現在讓我們看看 Linux 路徑的所有可能模式,並檢視是否得到了正確的輸出。
輸入 |
預期輸出 |
|---|---|
“/tmp/dir/target“ |
“target“ |
“/tmp/dir/target/“ |
“target“ |
“/“ |
“/“ |
“/tmp/dir/ “ |
” “ |
“/tmp/dir/ /“ |
” “ |
我們仍然可以擴充套件 awk 命令來涵蓋所有情況,或者為該任務編寫一個 bash 指令碼。
我們在這裡使用 awk 的單行命令作為示例:
$ awk -F'/' '$0==FS{ print $0; next }{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/tmp/dir/target"
target
$ awk -F'/' '$0==FS{ print $0; next }{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/tmp/dir/target/"
target
$ awk -F'/' '$0==FS{ print $0; next }{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/"
/
$ echo "^$( awk -F'/' '$0==FS{ print $0; next }{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/tmp/dir/ " )\$"
^ $
$ echo "^$( awk -F'/' '$0==FS{ print $0; next }{ a = length($NF) ? $NF : $(NF-1); print a }' <<< "/tmp/dir/ /" )\$"
^ $
我們使用 ^ 和 $ 指示預期結果的列印位置。
我們可以看到,awk 單行命令適用於所有情況,但與第一個版本(awk -F’/' '{printf "%s",$NF}')相比,它們現在相當複雜。
實際上,coreutils 包提供了一個方便的命令來解決我們的問題。
使用 basename 命令
basename 命令從給定的路徑字串中剝離目錄名。
此外,它相當穩定,並且涵蓋了所有邊緣情況。現在讓我們使用不同的輸入值進行一些測試。
$ basename "/tmp/dir/target" target $ basename "/tmp/dir/target/" target $ basename "/" / $ echo "^$(basename '/tmp/dir/ ')\$" ^ $ $ echo "^$(basename '/tmp/dir/ /')\$" ^ $
basename 命令透過重新命名檔案來解決問題。
您可能想提到 basename 命令(它剝離最後一個元件)有一個名為 dirnme 的同級(它刪除第一個元件)。
$ dirname "/tmp/dir/target" /tmp/dir
如果我們想處理路徑,我們可以首先考慮 basename 和/或目錄名是否可以解決我們的問題。通常,使用這兩個命令的解決方案是穩定的,並且更容易閱讀。
Awk 是強大的工具,但它們並不總是涵蓋所有情況。如果您在指令碼中使用它們,請注意不要忽略任何邊緣情況。
結論
我們探討了從路徑字串中提取最後一個元件的問題。
這個簡單的問題有多種解決方案。我們找到了一個涵蓋所有這些情況的 awk 單行命令。
我們還討論了一種更簡單的解決問題的方法:使用 basename 函式
資料結構
網路
RDBMS
作業系統
Java
iOS
HTML
CSS
Android
Python
C 程式設計
C++
C#
MongoDB
MySQL
Javascript
PHP