Linux または Windows で、そのディレクトリ内にある唯一の mp3 すべてへのパスを見つけるにはどうすればよいでしょうか?

Linux または Windows で、そのディレクトリ内にある唯一の mp3 すべてへのパスを見つけるにはどうすればよいでしょうか?

ミュージックフォルダがあり、その内容は次のようになります。

音楽
    -アーティストA
        -Song1.mp3
        -アルバム
            -Song2.mp3
            -ソング2.m4a
        -アルバムB
            -Song1.mp3
            -Song2.mp3
            -Song3.mp3
        -アルバムC
            -Song1.mp3
            -Song1.jpg
            -フォルダ1
                -Song1.mp3
    -アーティストB
        -Song1.mp3
    -アーティストC
        -Song1.mp3
        -Song2.mp3
        -Song3.mp3
        -フォルダ2
            -Song1.jpg
            -Song2.jpg
        -フォルダ3


次のような出力を探しています:

アーティストA/曲1.mp3
アーティストA/アルバムA/曲2.mp3
アーティストA/アルバムC/曲1.mp3
アーティストA/アルバムC/フォルダ1/曲1.mp3
アーティストB/曲1.mp3



したがって、私は 1 つのファイルだけを含むディレクトリではなく、サブディレクトリや他のファイルに関係なく、1 つの mp3 だけを含むディレクトリが必要です。また、空のディレクトリや、複数の mp3 を含むディレクトリも必要ありません。Linux

用のシェル スクリプトに反対しているわけではありません。ただし、どちらの OS でもプログラムをコンパイルしたくはありません。

試してみました find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

が、次のような出力が返されます:


./アーティストB
./アーティストA
./アーティストA/アルバムA
./アーティストA/アルバムC
./アーティストA/アルバムC/フォルダ1
./アーティストC/フォルダ2
./アーティストC/フォルダ3

近いですが、まさに私が望んでいるものではありません。空のフォルダーが 2 つあり、ファイル名はありません。

答え1

*.mp3一見すると、最初のコマンドが空のフォルダ(ファイルのないフォルダ)を報告している理由は、-le 1ではなく と言っているからです-eq 1。これは、1 つ以下の MP3 ファイルがあるディレクトリを報告するように見えます。しかし、-le 1を に変更して-eq 1も機能しません。問題は、1 つ以上の MP3 ファイルがあるディレクトリでは directory_name/*.mp3MP3 ファイル名のリストが取得されますが、1 つも MP3 ファイルがないディレクトリでは、文字通り のままであるということですdirectory_name/*.mp3。これは、どのファイルにも一致しないワイルドカードがコマンド ラインから消えるようにシェルに指示することで修正できます。

検索 . -type d -exec
        sh -c 'shopt -s ヌルグロブ;設定 -- "$0"/*.mp3; [ $# -eq 1 ]' {} \; -print

(全てを1行で入力すると)

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

しかし、シェルはファイルを数え、find印刷を要求しており、findディレクトリのみを参照しています。シェルが認識したファイル名を印刷するだけでは不十分ではないでしょうか。

検索 . -type d -exec
        sh -c 'shopt -s ヌルグロブ;設定 -- "$0"/*.mp3; [ $# -eq 1 ]&& エコー "$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} 

各ディレクトリ内のファイルマスクに一致する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

結果は同じですが、各フォルダーを 1 回だけチェックするため、時間を大幅に節約できます。awk コマンドは重複するフォルダー名を削除します。

.MP3もう1つ注意すべき点は、Linuxではファイルは大文字と小文字が区別されるため、またはファイルは完全に無視されるということです。これらを一致させたい場合は、の3つのインスタンスすべてを次のように.Mp3変更してください。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

関連情報