Copie os arquivos adicionando o caminho ao nome do arquivo

1

Eu quero copiar arquivos mp3 de um diretório para uma unidade flash. Eles são armazenados em uma estrutura de diretórios como esta: Artist/Album/Track.mp3 . Como o mp3 player do meu carro é uma porcaria, quero ter todos os meus arquivos de mp3 no diretório raiz da unidade flash, com o nome do arquivo no formato Artist-Album-Track.mp3 . Como posso copiar arquivos do diretório para a unidade flash, enquanto adiciono o caminho ao nome do arquivo?

    
por user394 08.07.2018 / 03:38

4 respostas

2

Um simples loop for poderia fazer isso, com expansões de parâmetros para extrair os vários elementos:

for file in */*/*.mp3;
do
  artist=${file%%/*}
  rest=${file#*/}
  album=${rest%%/*}
  track=${rest#*/*}
  cp -- "$file" /flash/drive/"${artist}-${album}-${track}"
done

A primeira expansão retira tudo, desde o final do arquivo até a primeira barra invertida - gerando o artista.

A segunda expansão retira os caracteres iniciais até a primeira barra invertida - tirando o artista do caminho.

A terceira expansão é como a primeira, tirando o nome do arquivo do caminho restante, deixando o álbum.

A quarta expansão é como a segunda, retirando o álbum, deixando apenas o nome do arquivo.

Depois, juntamos tudo de novo com traços e copiamos para o caminho desejado / flash / drive.

    
por 08.07.2018 / 04:18
1

Usando find e bash para copiar os arquivos de $mp3dir para $destdir :

mp3dir="$HOME/my_music"
destdir="/mnt/my_mp3_player"

find "$mp3dir" -type f -name '*.mp3' -exec bash -c '
    mp3dir=$1; destdir=$2; shift 2
    for pathname do
        dest=${pathname#$mp3dir/}
        dest="$destdir/${dest//\//-}"
        if [ -f "$dest" ]; then
            printf "%s exist, skipping %s\n" "$dest" "$pathname" >&2
        else
            cp "$pathname" "$dest"
        fi
    done' bash "$mp3dir" "$destdir" {} +

Isso procura por qualquer arquivo regular dentro ou abaixo de $mp3dir cujos nomes terminam com .mp3 . Para lotes desses, um script curto é executado. O par de parâmetros para o script é $mp3dir e $destdir e o resto são nomes de caminho de arquivos MP3.

O script seleciona dois nomes de diretórios dos dois primeiros argumentos de linha de comando e, em seguida, percorre o restante dos argumentos, construindo o nome do caminho de destino como $dest para cada arquivo MP3. Isso é feito usando uma substituição do parâmetro bash que substitui todas as barras no nome do caminho por traços após remover o bit $mp3dir inicial do nome do caminho.

Se $dest já existir, uma mensagem sobre isso será impressa, caso contrário, o arquivo será copiado.

Relacionados:

por 08.07.2018 / 15:06
1

Eu acredito que é uma prática ruim manipular várias strings com expansões de parâmetros de shell e atribuições de variáveis.

root_dir="~/songs"
for fn in */*/*.mp3  
do 
   cp -v "$fn" "${root_dir}/${fn////-}"    # replace all slashes by dashes
done

Saída:

~/songs/Artist-Album-Track.mp3
    
por 08.07.2018 / 12:08
0

Antecedentes

Eu estava curioso para saber se conseguiria uma solução que alcançasse dois objetivos:

  • mais fácil de ler
  • mais fácil de depurar

Construindo a maravilhosa resposta de @kusalananda a este outro Q & A de Q & A intitulado: Compreendendo a opção -exec de find , acho que descobri algo que resolve seu problema.

Para começar, aqui está minha estrutura de diretório de amostra que imita a sua.

$ mkdir -p Artist{1..5}/Album{1..5}
$ touch Artist{1..5}/Album{1..5}/Track{1..5}.mp3

Isso resulta no seguinte:

$ find Artist* -type f | head
Artist1/Album1/Track1.mp3
Artist1/Album1/Track2.mp3
Artist1/Album1/Track3.mp3
Artist1/Album1/Track4.mp3
Artist1/Album1/Track5.mp3
Artist1/Album2/Track1.mp3
Artist1/Album2/Track2.mp3
Artist1/Album2/Track3.mp3
Artist1/Album2/Track4.mp3
Artist1/Album2/Track5.mp3

Agora, copie seus arquivos de mp3 para ./targetDir :

$ find . -type f -name '*.mp3' -exec sh -c \
    'cp {} targetDir/$(echo "{}" | sed "s#\./##g;s#/#-#g")' \;

O que resulta disso:

$ ls targetDir/ | head
Artist1-Album1-Track1.mp3
Artist1-Album1-Track2.mp3
Artist1-Album1-Track3.mp3
Artist1-Album1-Track4.mp3
Artist1-Album1-Track5.mp3
Artist1-Album2-Track1.mp3
Artist1-Album2-Track2.mp3
Artist1-Album2-Track3.mp3
Artist1-Album2-Track4.mp3
Artist1-Album2-Track5.mp3

Eu gosto dessa abordagem, porque posso envolver o comando cp ... em echo primeiro para poder verificar a saída antes de confirmar o trabalho:

$ find . -type f -name '*.mp3' -exec sh -c \
    'echo "cp {} targetDir/$(echo "{}" | sed "s#\./##g;s#/#-#g")"' \;
cp ./Artist1/Album1/Track1.mp3 targetDir/Artist1-Album1-Track1.mp3
cp ./Artist1/Album1/Track2.mp3 targetDir/Artist1-Album1-Track2.mp3
cp ./Artist1/Album1/Track3.mp3 targetDir/Artist1-Album1-Track3.mp3
cp ./Artist1/Album1/Track4.mp3 targetDir/Artist1-Album1-Track4.mp3
cp ./Artist1/Album1/Track5.mp3 targetDir/Artist1-Album1-Track5.mp3
cp ./Artist1/Album2/Track1.mp3 targetDir/Artist1-Album2-Track1.mp3
cp ./Artist1/Album2/Track2.mp3 targetDir/Artist1-Album2-Track2.mp3
cp ./Artist1/Album2/Track3.mp3 targetDir/Artist1-Album2-Track3.mp3
cp ./Artist1/Album2/Track4.mp3 targetDir/Artist1-Album2-Track4.mp3
...

Como funciona

Esta solução recebe a saída de find e, em seguida, executa o seguinte comando shell:

cp {} targetDir/$(echo "{}" | sed "s#\./##g;s#/#-#g")'

Isso irá cp do arquivo de Artist../Album../Track.. para targetDir/.. e reformulará o nome para que ele tenha traços ( - ) onde há uma barra ( / ).

NOTA: adicionei 2 operações ao sed . A primeira tira qualquer prefixo ./ que possa existir se você usar find . ... em vez de find Artist* .. .

Alternativas?

Acho que fazer isso usando find e cp ainda não é a solução ideal. Eu também mantenho diretórios de arquivos MP3 e acho que fazer algo parecido com rsync e através de listas que você fornece rsync pode ser uma implementação mais útil, mais longo desde que você pode usá-lo para atualizar, em vez de copiar cada vez que você executá-lo.

$ man rsync
...
        --files-from=FILE       read list of source-file names from FILE

Apenas um pensamento.

    
por 08.07.2018 / 16:52

Tags