從給定路徑中查詢最後一個目錄或檔案


概述

在處理路徑時,我們經常使用 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 函式

更新於: 2022-12-23

628 次檢視

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.