Bash copia todos os arquivos que não correspondem às extensões dadas

6

Quando eu importo fotos da minha câmera no Shotwell, ele também importa os clipes de vídeo. Isso é um pouco irritante, pois eu gostaria de armazenar meus vídeos em outra pasta. Eu tentei escrever um comando bash para fazer isso, mas não tive sucesso.

Eu preciso de um comando que atenda aos seguintes requisitos:

  • Localize todos os arquivos em uma estrutura de diretório que não tenha uma extensão .jpg, .png, .gif ou .xcf (insensível a maiúsculas e minúsculas).
  • Mova todos esses arquivos para um diretório de destino, independentemente de os nomes de arquivos ou os caminhos de diretório contiverem espaços ou caracteres especiais.

Qualquer ajuda seria apreciada!

EDIT: Estou usando o shell padrão no Ubuntu, o que significa que alguns comandos são com alias, etc.

EDIT 2: Eu tentei isso sozinho (não a parte da cópia, apenas a listagem dos arquivos). Liguei o extglob e executei o seguinte comando:

$ ls -R /path | awk '
  /:$/&&f{s=$0;f=0}
  /:$/&&!f{sub(/:$/,"");s=$0;f=1;next}
  NF&&f{ print s"/"$0 }'

Isso lista tudo. Eu tentei usar o grep no final, mas não tenho a menor idéia de como obtê-lo para não corresponder a um padrão que lhe dei. O switch extglob não ajudou muito com o grep, apesar de ajudar com outros comandos.

    
por Adam 04.08.2014 / 04:45

2 respostas

10

Você pode usar find para encontrar todos os arquivos em uma árvore de diretórios que correspondam (ou não jogo) alguns testes particulares e, em seguida, fazer algo com eles. Para este problema em particular, você poderia usar:

find -type f ! \( -iname '*.png' -o -iname '*.gif' -o -iname '*.jpg' -o -iname '*.xcf' \) -exec echo mv {} /new/path \;

Isso limita a pesquisa a arquivos regulares ( -type f ) e, em seguida, a arquivos cujos nomes não ( ! ) têm a extensão *.png em qualquer caixa ( -iname '*.png' ) ou ( -o ) *.gif e assim por diante. Todas as extensões são agrupadas em uma única condição entre \( ... \) . Para cada arquivo correspondente, ele executa um comando ( -exec ) que move o arquivo, cujo nome é inserido no lugar de {} , no diretório /new/path . O \; informa find que o comando acabou.

A substituição de nomes acontece dentro do código de execução do programa, portanto, espaços e outros caracteres especiais não importam.

Se você quiser fazer isso apenas dentro do Bash, é possível usar a correspondência de padrões estendida do Bash recursos . Eles exigem que shopt extglob esteja ativado e globstar também. Neste caso, use:

mv **/!(*.[gG][iI][fF]|*.[pP][nN][gG]|*.[xX][cC][fF]|*.[jJ][pP][gG]) /new/path

Isso corresponde a todos os arquivos em subdiretórios ( ** ) que não correspondem a *.gif , *.png , etc, em qualquer combinação de casos de caractere e os move para o novo caminho. A expansão é executada pelo shell, portanto, espaços e caracteres especiais não importam novamente.

O acima assume que todos os arquivos estão em subdiretórios. Se não, você pode repetir a parte depois de **/ para incluir o diretório atual também.

Existem recursos semelhantes em zsh e em outros shells, mas você ve indicou que você está usando Bash.

(Uma observação adicional: analisar ls nunca é uma boa ideia - apenas não tente.)

    
por 04.08.2014 / 05:05
1

Você usa o Ubuntu, então você encontrará o GNU, tente:

find . -maxdepth 1 ! -iregex ".*\.\(jpg\|png\|gif\|xcf\)$" -exec mv -- -t /path/to/newdir "{}" +
  • -iregex usa o regex para localizar o nome do arquivo, mas não faz distinção entre maiúsculas e minúsculas, ! nega o regex.

  • -exec command + run comando para correspondência de arquivos. É como usar find -print0 com xargs -0 . Usando isso, podemos mover vários arquivos encontrados com um comando mv , portanto, o número total de comandos de chamada é menor que o número de arquivos correspondentes.

  • -maxdepth 1 limit find para pesquisar apenas no diretório atual, se você quiser recursiva, remova-a.

por 04.08.2014 / 05:14