
ミュージックフォルダがあり、その内容は次のようになります。
音楽 -アーティスト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/*.mp3
MP3 ファイル名のリストが取得されますが、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