在 Linux 或 Windows 上,如何找到該目錄中唯一 mp3 的所有 mp3 的路徑?

在 Linux 或 Windows 上,如何找到該目錄中唯一 mp3 的所有 mp3 的路徑?

我有一個 Music 資料夾,內容如下:

音樂
    -藝術家A
        -歌曲1.mp3
        -專輯A
            -歌曲2.mp3
            -歌曲2.m4a
        -專輯B
            -歌曲1.mp3
            -歌曲2.mp3
            -歌曲3.mp3
        -專輯C
            -歌曲1.mp3
            -歌曲1.jpg
            -SomeFolder1
                -歌曲1.mp3
    -藝術家B
        -歌曲1.mp3
    -藝術家C
        -歌曲1.mp3
        -歌曲2.mp3
        -歌曲3.mp3
        -SomeFolder2
            -歌曲1.jpg
            -Song2.jpg
        -SomeFolder3


我正在尋找這樣的輸出:

藝人A/歌曲1.mp3
藝術家A/AlbumA/Song2.mp3
藝人A/專輯C/Song1.mp3
藝術家A/AlbumC/SomeFolder1/Song1.mp3
藝人B/歌曲1.mp3



所以,我不想要只有一個檔案的目錄,我想要只有一個 mp3 的目錄 - 無論子目錄或其他檔案如何。我也不想要空目錄或包含多個 mp3 的目錄。

我並不反對 Linux 上的 shell 腳本。但是,我不想在任何一個作業系統上編譯程式。

我已經嘗試過: find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

但這給了我這個輸出:


./藝術家B
./藝術家A
./藝術家A/專輯A
./藝術家A/專輯C
./ArtistA/AlbumC/SomeFolder1
./ArtistC/SomeFolder2
./ArtistC/SomeFolder3

這很接近,但不完全是我想要的。有兩個空資料夾,沒有檔案名稱。

答案1

乍一看,您的第一個命令報告空資料夾(沒有*.mp3文件的資料夾)的原因是您說的-le 1是而不是-eq 1.看起來它會報告包含一個或更少 MP3 檔案的目錄。但改成-le 1不行-eq 1。事實證明,問題是,在一個包含 ≥ 一個 MP3 檔案的目錄中, directory_name/*.mp3會為您提供 MP3 檔案名稱的列表,但是,在沒有任何目錄的目錄中,它仍然保留為directory_name/*.mp3.可以透過告訴 shell 不匹配任何檔案的通配符應該從命令列中消失來解決這個問題:

尋找 。 -type d -exec
        sh-c'shopt -s nullglob;設定--“$0”/*.mp3; [ $# -eq 1 ]' {} \; -列印

(全部輸入為一行)產量

./ArtistA
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistB

但是您已經讓 shell 計算檔案數,並且您要求find進行列印,並且find只查看目錄。為什麼不讓 shell 列印它看到的檔案名稱呢?

尋找 。 -type d -exec
        sh -c 'shopt -s nullglob;設定--“$0”/*.mp3; [ $# -eq 1 ]&& 回顯“$1”' {} \;

(即,如果有一個文件,echo第一個文件的名稱 ( $1))將為您提供您想要的內容。

答案2

對於使用 PowerShell 的 Windows,假設您已位於資料夾 C:\YOUR_PATH 中:

PS C:\YOUR_PATH> (Get-ChildItem -recurse | Where-Object {$_.name -match ".mp3"}) | Group-Object -Property Directory | Where-Object {$_.Count -eq 1} | ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName}

輸出:

C:\YOUR_PATH\ArtistA\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumA\Song2.mp3
C:\YOUR_PATH\ArtistA\AlbumC\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumC\SomeFolder1/Song1.mp3
C:\YOUR_PATH\ArtistB\Song1.mp3

分解管道命令:

取得目前目錄下的所有檔案和資料夾

Get-ChildItem -recurse 

僅匹配基於您的遮罩的文件

Where-Object {$_.name -match ".mp3"})

根據目錄屬性執行分組

Group-Object -Property Directory 

僅選擇包含 1 項的目錄

Where-Object {$_.Count -eq 1} 

提取每個目錄中與您的檔案遮罩相符的一個檔案的全名

ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName} 

答案3

我確信有人有更好的方法,但這個 bash 命令可以在 Linux 上運行。

for Folder in $(find . -name "*.mp3" -printf "%h\n")
do
    [[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
done

此腳本對每個 .mp3 檔案的資料夾執行測試,對相同檔案的每次匹配重複自身。例如,它檢查 Music/ArtistA/AlbumB 3 次。根據您擁有的 mp3 檔案數量,這可能需要很長時間(您需要一個相當大的庫)。

如果是這樣,你可以讓它稍微複雜一些:

for Folder in $(find . -name "*.mp3" -printf "%h\n" | awk '!x[$0]++')
    do
        [[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
    done

結果是相同的,但它只會檢查每個資料夾一次,這可以節省大量時間。 awk 指令刪除任何重複的資料夾名稱。

另一個注意事項是 Linux 上的檔案區分大小寫,因此這將完全忽略任何.MP3.Mp3檔案。如果您希望它與這些匹配,請將所有 3 個實例更改mp3[mM][pP]3

答案4

這是一個答案:

find Music -name "*.mp3" -exec dirname {} \; |
    sort |
    uniq -c |
        while read count parent
        do
            if [[ $count -eq 1 ]]
            then
                echo "$parent"/*.mp3
            fi
        done

相關內容