特定の種類のxファイルを含むフォルダを見つけて、OSXでそれらのパスを出力するにはどうすればいいですか

特定の種類のxファイルを含むフォルダを見つけて、OSXでそれらのパスを出力するにはどうすればいいですか

私はOSX用のスクリプトを持っていて、1つのファイルのみを含むフォルダを見つけ、そのファイルがオーディオファイルであればオーディオファイルのパスを出力します。

find "$1" -type d -exec sh -c '[[ $(find "$0" -mindepth 1 | wc -l) -eq 1 ]] 
&& [[ $(find "$0" -mindepth 1 -type d | wc -l) -eq 0 ]]  
&& find "$0"' {} \; |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"

例えば、

./findodd.sh /Users/paul/Music

しかし、改善が必要な点が 2 つあります。

  1. 2つのファイル、3つのファイルなどを含むフォルダー内のファイルをリストするように変更するにはどうすればよいですか?これをパラメーターとして渡すことができればさらに良いでしょう。

  2. 現在、1 つのファイルのみを含むフォルダーが検索され、そのファイルはオーディオ ファイルである必要があります。ただし、実際に実行したいのは、1 つのオーディオ ファイルのみを含むフォルダーを検索することです。つまり、フォルダーに 3 つのファイルが含まれていて、そのうち 1 つだけがオーディオ ファイルである場合、そのオーディオ ファイルを一覧表示したいのです。

ありがとう、ポール

答え1

$ find
.
./folder3
./folder3/quux.txt
./folder1
./folder1/test.mp3
./folder1/test.txt
./folder1/test.wma
./folder2
./folder2/bar.txt
./folder2/foo.txt
./folder2/test.ogg

実行例:

$ ./findaudio.sh /tmp/findaudio 1
/tmp/findaudio/folder2/test.ogg

$ ./findaudio.sh /tmp/findaudio 2
/tmp/findaudio/folder1/test.mp3
/tmp/findaudio/folder1/test.wma

# The first parameter defaults to the current directory and
# the second parameter defaults to 1 so this works as well:
$ ./findaudio.sh
./folder2/test.ogg

コードは次のとおりです:

#!/bin/bash

shopt -s nullglob

find "${1:-.}" -type d | while read dir; do
        files=( "${dir}"/*.{mp4,mp3,ogg,flac,wma,m4a} )
        IFS=$'\n'
        (( ${#files[@]} == ${2:-1} )) && echo "${files[*]}"
done

指定されたディレクトリのすべてのサブディレクトリを反復処理し、グロブを使用して現在のサブディレクトリのすべてのオーディオ ファイル名を配列に読み込みますfiles。配列のサイズが目的の値と一致する場合は、改行で区切られたファイル名が出力されます。

編集: これは、問題のファイル名ではなくフォルダーを印刷したいという前提に基づいた、以前のアプローチです。今後の参考のためにここに残しておきます。

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -u
./folder2

これは、リストされているオーディオ拡張子を持つすべてのファイルを検索し、フルパスではなくディレクトリ コンポーネントのみを出力します。これにより、すべてのオーディオ ファイルの親フォルダーのリストが表示されます。一意uniqでない行はスキップされるため、必要な結果が得られます (つまり、オーディオ ファイルが 1 つだけ含まれるフォルダーのみが出力されます)。

理論的には、これも以前の試みよりもかなり速くなるはずです。

重複行をカウントし、要求された数に一致するフォルダーのみを印刷することで、これを改善して最初のポイントを満たすことができます。単純な解決策は次のようになります。

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -c | awk -v count=1 '$1==count'
1 ./folder2

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -c | awk -v count=2 '$1==count'
2 ./folder1

uniqただし、パイプの - 部分と右側を 1 本の線で融合する方がよい場合がありますawk

答え2

2度目の試み

わかりました。実際に自分のミュージック フォルダーでこれを試してみたところ、両方の要求に対する解決策は次のようになりました。

COMMAND='[[ $(find "$0" -maxdepth 2 |egrep "\.mp4|\.mp3|\.ogg|\.flac|\.wma|\.m4a"| wc -l) == '$2' ]] && echo "$0"'
find $1 -type d -exec sh -c "$COMMAND" {} \;

つまり、スクリプトにはいくつか間違いがありました:

  1. mindepthの代わりにを使用していましたmaxdepth
  2. egrep 内のピリオド (.) は任意の文字と一致します。したがって、.wma「Snowman.txt」と一致します。
  3. シェル コマンドにはディレクトリのみが渡されるため、タイプ 'd' の 2 番目のテストを実行する必要はありません。

私のスクリプトに関するメモ:

  1. 使用方法は次のとおりです。findodd.sh <top_folder> <no_of_files>
  2. 引用符は重要です。 の定義は、COMMAND実際には の両側にある 2 つの文字列リテラルです$2。これは本当に重要です。
  3. ファイルを含むフォルダーのみがリストされ、ファイル自体はリストされません。ファイル自体を表示するには、 をecho "$0"別の に置き換える必要がありますfind

現在、私は Arch Linux マシンでテストしており、シェルは「bash」です。すべてのシェルが同じように作成されているわけではないので、これが OSX で動作するかどうかはわかりません。:-)


以前の最初の試み:

うーん。OSX が Unix/Linux とどの程度似ているかはわかりませんが、試してみます。

あなたの質問の両方に対する答えは、'sh -c' コマンドの最初のテストにあると私は信じています。それは次の部分です:

$(find "$0" -mindepth 1 | wc -l) -eq 1

ファイル数を表す 2 番目のパラメータをスクリプトに渡すには、'1' を $2 に変更するだけでよいので、テストは次のようになります。

$(find "$0" -mindepth 1 | wc -l) -eq $2

引用符を付けないでください。$2そうしないと、スクリプトではなく、'sh -c' コマンドに渡される 2 番目のパラメータとして解釈されます。

コマンドラインは次のようになります。

./findodd.sh /Users/paul/Music 2

egrep私の理解では、2 番目の要件を満たすには、コマンドを最初のテストに組み込む必要があります。

$(find "$0" -mindepth 1 |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"| wc -l) -eq $2

ただし、引用文には注意する必要があるかもしれません。

とにかく、試してみて、感想を聞かせてください。

答え3

これを Python で実装するには、次のようにします。

#!/usr/bin/env python

import fnmatch
import os
import sys

if len(sys.argv) != 3 or \
        not sys.argv[1].isdigit() or \
        not os.path.exists(sys.argv[2]):
    print "Usage: %s [number of files] [search root]" % sys.argv[0]
    sys.exit(1)

num_files = int(sys.argv[1])
search_root = sys.argv[2]

# this must be a tuple to work with endswith()
audio_extensions = (
    'mp4',
    'mp3',
    'ogg',
    'flac',
    'wma',
    'm4a',
)

for dirpath, dirnames, filenames in os.walk(search_root):
    audio_files = [f for f in filenames if f.endswith(audio_extensions)]
    if len(audio_files) == num_files:
        print "\n".join([os.path.join(dirpath, f) for f in audio_files])

その後chmod +x findodd.py、現在のスクリプトを実行するのと同じ方法で実行できます。例:

./findodd.py 1 /Users/paul/Music

関連情報