2

У меня есть этот скрипт для OSX для поиска папок, которые содержат только один файл, и если этот файл является аудиофайлом с выводом пути аудиофайла

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

но есть два улучшения, которые мне нужны:

  1. Что я могу изменить, чтобы он отображал файлы в папках, содержащих 2 файла, 3 файла ectera, было бы еще лучше, если бы это можно было передать в качестве параметра

  2. В настоящее время он находит папки, содержащие только один файл, и этот файл должен быть аудиофайлом. Но я действительно хочу найти папку, содержащую только один аудиофайл, т.е. если папка содержит три файла, но только один является аудиофайлом, я хотите, чтобы этот аудиофайл был в списке.

спасибо Пол

3 ответа3

2
$ 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 пропускает неуникальные строки, которые должны дать результат, к которому вы стремитесь, т.е. только папки печати, которые содержат ровно один аудиофайл.

Теоретически это также должно быть немного быстрее, чем ваши предыдущие попытки.

Вы можете улучшить это, чтобы удовлетворить свою первую точку, подсчитав дубликаты строк и распечатав только те папки, которые соответствуют запрошенному количеству. Наивным решением будет:

$ 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 -part и правую часть трубы в одну строку awk .

1

ВТОРАЯ ПОПЫТКА

Хорошо, после того, как я сам попробую это сделать в моей собственной папке Music, это решение обеих ваших запросов:

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», поскольку в команду оболочки передаются только каталоги.

Примечания к моему сценарию:

  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

Чтобы передать второй параметр в ваш скрипт для количества файлов, вы должны просто изменить «1» на $ 2, так что тест будет:

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

Не ставьте кавычки вокруг $2 потому что в противном случае он будет интерпретироваться как второй параметр, передаваемый команде sh -c, а не вашему сценарию.

Командная строка будет тогда:

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

Чтобы выполнить ваше второе требование, насколько я понимаю, вам нужно поместить команду egrep в этот первый тест, таким образом:

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

Возможно, вам придется смотреть цитаты, хотя.

В любом случае, попробуй и дай нам знать.

0

Вы можете реализовать это в 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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .