Optimize find -exec {} com várias condições: arquivos específicos em um diretório dir e subdiretórios específicos neste diretório

1

Meu problema está relacionado à codificação de músicas no meu NAS e à otimização do comando linux find .

Eu escrevi um script que é totalmente funcional, mas tenho certeza que não é o melhor jeito.

Para resumir:

1 / Verificações de script para diretórios que contenham flac para codificar

  • todos os diretórios contendo alguns .flac arquivos
  • os diretórios NÃO contêm um subdiretório denominado mp3/ ou Mp3/

Para essa parte específica, fiz algo assim:

FILE="./DirContaininfFlacToConvertToMp3.txt"
find . -type f -iname '*.flac' -printf '%h\n' | sort -u > fic1
find . -iname 'mp3' -type d | sort | sed 's/\/Mp3//' | sed 's/\/mp3//' > fic2
comm -3 fic1 fic2 > $FILE
  • O $ FILE será preenchido com todos os diretórios correspondentes a essas duas condições.
  • O arquivo fic1 contém todos os diretórios contendo arquivos flac.
  • O arquivo fic2 contém todos os diretórios contendo o subdiretório mp3 (o sed é usado para eliminar mp3/ ou Mp3/ no final do caminho para o comando comm funcionar)
  • comm -3 me fornece meu resultado em $FILE

Funciona tudo bem, mas tenho certeza de que essa não é a melhor maneira de fazer, e pode ser otimizada apenas usando os parâmetros find ou passando para o shell ..

2 / Script Codifica os arquivos flac e move para um subdiretório Mp3 /

Quando o arquivo ( $FILE ) fornecer o resultado do diretório que corresponde às condições das próximas etapas, faça o seguinte:

  • Eu codifico os arquivos em mp3 no diretório definido
  • movê-los para um subdiretório denominado Mp3 (mantendo a estrutura original)

Para essa parte eu fiz isso em 2 loops:

#1st loop to encode
IFS=$'\n'
for next in 'cat $FILE'
do
  find $next -type f  -name "*.flac" -exec ffmpeg -i {} -qscale:a 2 -map_metadata 0 -id3v2_version 3 {}.mp3 \;
done

#2nd loop for moving to subdir
for next in 'cat $FILE'
do
  find $next -type f -name '*.flac.mp3' -execdir mkdir -p Mp3 \; -execdir mv {} Mp3/ \;
done

Também aqui tenho certeza que eu poderia evitar iterar em 2 loops.

No entanto, você pode encontrar para qualquer propósito o script completo aqui (não muito bem codificado eu sei ...: - (

Script completo

#!/bin/bash
# Convert .flac en .mp3 file for Synology with ffmpeg
# usage : 1st construct list of directory to convert : run:  ./script.sh V
#         2nd verify and to convert run : nohup ./script C & 

#result of list of directories to encode
FILE="./ToConvertToMp3.txt"

# parameter validation
if [ "$#" -ne  "1" ]
  then
     echo "1 parameter required : ./script.sh (V)erify (C)onvert "
 exit 1;
fi


# generate list of directory for encoding
if [ "$1" ==  "V" ]
  then
    echo "-------- VERIFY AND CONSTRUCT LIST -------"
    #1 find dir that contains .flac file
    find . -type f -iname '*.flac' -printf '%h\n' | sort -u > fic1

    # for those directory search if there is some *mp3* subdir
    IFS=$'\n'
    for dirflac in 'cat fic1'
    do
      find $dirflac -iname '*mp3*' -type d -exec dirname {} \; >> fic2
    done

    # sort and unicity
    cat fic2 | sort -u > fic3

    # final construc of the list of directory to encode
    comm -3 fic1 fic2 > $FILE
    rm -f fic1 fic2 fic3

    echo "-----DIRECTORY TO CONVERT-------"
    cat $FILE
    echo "-------------------------------------"
    exit 0
fi

# Converting .flav files from directories listed in $FILE
if [ "$1" ==  "C" ]
  then
    echo "-------- CONVERTING FLAC -------"

    if [ -e $FILE ]; then
      echo "$FILE exist : OK"
    else
      echo "$FILE does not exist Please (V)erification first"
    exit 3
    fi

  if [ -s "$FILE" ]; then
    echo "$FILE contains directories : OK"
    cat $FILE
  else
    echo "$FILE is empty , nothing to do"
    exit 2
  fi

  echo "-------- CONVERTING -------"
  IFS=$'\n'
  for next in 'cat $FILE'
  do
    find $next -type f  -name "*.flac" -exec ffmpeg -i {} -qscale:a 2 -map_metadata 0 -id3v2_version 3 {}.mp3 \;
    album="$(basename $directory)-Mp3";
    echo "  Processing $directory : creating subdirectory : $album, moving files in progress"
    find $directory -type f -name '*.flac.mp3' -execdir mkdir -p "$album" \; -execdir mv {} "$album/" \;

  done
  echo "-------- END OF CONVERSION -------"

  # renaming $FILE with timestamp
  ficdate=$(date +%Y%M%d-%Hh%m);
  mv $FILE $FILE-$ficdate.done;

  exit 0
fi

echo "WARNING : 1 parameter : usage : ./script.sh (V)erification (C)onversion"
    
por S. Di Cioccio 27.07.2016 / 15:23

1 resposta

1

Você poderia fazer:

find . -type f -name '*.flac' -execdir sh -c '
  if [ ! -d mp3 ] && [ ! -d Mp3 ]; then
    for file do
      ffmpeg -i "$file" -qscale:a 2 -map_metadata 0 -id3v2_version 3 "${file%.*}.mp3"
    done
  fi' sh {} +

A idéia é que, com -execdir cmd {} + , (com algumas versões do GNU find ), find executará cmd para todo o arquivo correspondente em um determinado diretório.

Eu digo algumas versões do GNU find porque ele costumava funcionar assim, mas foi quebrado em algumas versões de find (onde você obteria uma cmd invocação por arquivo como se você usou -execdir cmd {} \; ) e foi corrigido novamente em uma versão posterior.

Você pode verificar se tem uma versão correta com:

find . -execdir echo {} +

Você deve obter uma linha por diretório com uma versão correta ou uma linha por arquivo com as menos corretas.

Se você tiver uma versão correta e não tiver milhares de arquivos flac por diretório, poderá realizar as duas ações de uma só vez:

find . -type f -name '*.flac' -execdir sh -c '
  if [ ! -d mp3 ] && [ ! -d Mp3 ]; then
    mkdir Mp3 || exit
    for file do
      ffmpeg -i "$file" -qscale:a 2 -map_metadata 0 -id3v2_version 3 "Mp3/${file%.*}.mp3"
    done
  fi' sh {} +
    
por 27.07.2016 / 15:57