
我有一個 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