Tengo este script para OSX para buscar carpetas que solo contengan un archivo, y si ese archivo es un archivo de audio, genera la ruta del archivo de audio.
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"
es decir, usar como
./findodd.sh /Users/paul/Music
pero hay dos mejoras que necesito:
¿Qué puedo cambiar para que enumere los archivos en carpetas que contienen 2 archivos, 3 archivos, etc.? Sería incluso mejor si esto pudiera pasarse como parámetro.
Actualmente busca carpetas que contienen solo un archivo, y ese archivo debe ser un archivo de audio, pero lo que realmente quiero que haga es encontrar una carpeta que contenga solo un archivo de audio, es decir, si la carpeta contiene tres archivos pero solo uno es un archivo de audio, desea que ese archivo de audio aparezca en la lista.
gracias pablo
Respuesta1
$ 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
Ejecuciones de ejemplo:
$ ./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
Y aquí el código:
#!/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
Itera sobre todos los subdirectorios del directorio dado y utiliza globbing para leer todos los nombres de archivos de audio del subdirectorio actual en la matriz files
. Si el tamaño de la matriz coincide con el valor deseado, simplemente imprime los nombres de los archivos separados por una nueva línea.
EDITAR: Este es mi enfoque anterior basado en la suposición de que deseaba imprimir las carpetas, no los nombres de archivos en cuestión. Lo dejaré aquí para referencia futura.
$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -u
./folder2
Lo que esto hace es encontrar todos los archivos con las extensiones de audio enumeradas y solo imprimir los componentes de su directorio en lugar de la ruta completa. Esto le brinda una lista de carpetas principales para todos los archivos de audio. Salta uniq
líneas no únicas que deberían darle el resultado que busca, es decir, solo imprimir carpetas que contengan exactamente un archivo de audio.
En teoría, esto también debería ser bastante más rápido que el intento anterior.
Puede mejorar esto para satisfacer su primer punto contando las líneas duplicadas e imprimiendo solo las carpetas que coincidan con el recuento solicitado. Una solución ingenua sería:
$ 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
aunque podría ser mejor fusionar la uniq
parte - y el lado derecho de la tubería en una sola awk
línea.
Respuesta2
SEGUNDO INTENTO
Bien, después de probar esto yo mismo en mi propia carpeta de Música, esta es la solución para ambas solicitudes:
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" {} \;
Así que había algunas cosas mal en tu guión:
- Estabas usando
mindepth
en lugar demaxdepth
. - Los puntos (.) en su egrep habrían coincidido con cualquier carácter. Entonces
.wma
habría coincidido con 'Snowman.txt'. - No fue necesario realizar la segunda prueba para el tipo 'd' ya que solo se pasan directorios al comando de shell.
Notas sobre mi guión:
- El uso es:
findodd.sh <top_folder> <no_of_files>
- Las citas son fundamentales. La definición de
COMMAND
es en realidad 2 cadenas literales a cada lado del$2
. Eso es realmente importante. - Sólo enumera las carpetas que contienen los archivos, no los archivos en sí. Para hacer esto último, tendrías que reemplazarlo
echo "$0"
por otrofind
.
Ahora he estado probando en una máquina Arch Linux y mi shell es 'bash', así que no tengo idea si esto funcionará en OSX, ya que todos los shells NO son iguales. :-)
PRIMER INTENTO ANTES:
Mmmmm. No sé qué tan similar es OSX a Unix/Linux, pero lo intentaré.
Creo que la respuesta a ambas preguntas se encuentra en la primera prueba del comando 'sh -c'. Esa es la parte que dice:
$(find "$0" -mindepth 1 | wc -l) -eq 1
Para pasar un segundo parámetro a su secuencia de comandos para la cantidad de archivos, debería poder simplemente cambiar el '1' a $2, por lo que la prueba sería:
$(find "$0" -mindepth 1 | wc -l) -eq $2
No ponga comillas $2
porque, de lo contrario, se interpretará como el segundo parámetro pasado al comando 'sh -c', no como su script.
La línea de comando sería entonces:
./findodd.sh /Users/paul/Music 2
Para lograr su segundo requisito, según tengo entendido, debe poner el egrep
comando en esa primera prueba, así:
$(find "$0" -mindepth 1 |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"| wc -l) -eq $2
Aunque quizá tengas que tener cuidado con las citas.
De todos modos, pruébalo y háznoslo saber.
Respuesta3
Podrías implementar esto en Python haciendo algo como esto:
#!/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])
Si es así, chmod +x findodd.py
puede ejecutarlo de la misma manera que ejecuta su script actual, por ejemplo:
./findodd.py 1 /Users/paul/Music