localiza arquivos baseados no nome e move-os com renomeação simultaneamente

5

Considere que eu tenho diretórios nomeados de acordo com anos e contendo arquivos pdf de acordo com o assunto-código.

ls output é:

$ ls -l
total 32
drwxrwxrwx 1 root root 4096 Apr  8 08:52 May-June-2011
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Nov-Dec-2011
drwxrwxrwx 1 root root 4096 Apr  8 20:36 Summer-2012
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Summer-2013
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Summer-2014
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Winter-2012
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Winter-2013
drwxrwxrwx 1 root root 4096 Apr  8 08:52 Winter-2014

Cada diretório contém arquivos pdf de acordo com o código do assunto: -

ls -l May-June-2011/
total 808
-rwxrwxrwx 1 root root 104193 May  1  2011 161901.pdf
-rwxrwxrwx 1 root root 103380 May  1  2011 161902.pdf
-rwxrwxrwx 1 root root 115664 May  1  2011 161903.pdf
-rwxrwxrwx 1 root root  88953 May  1  2011 161904.pdf
-rwxrwxrwx 1 root root 179268 May  1  2011 161905.pdf
-rwxrwxrwx 1 root root 116158 May 24  2011 161906.pdf
-rwxrwxrwx 1 root root 106033 May  1  2011 161907.pdf

Em outras palavras, cada diretório possui 16190{1..7}.pdf

Agora, suponha que eu queira mover todos (de todos os diretórios mencionados) 161901.pdf para um diretório específico (digamos xyz ) com renomeação para o nome da pasta pai.pdf.

Explicação: -

Aqui está a lista de todos os 161901.pdf :

$ find -name 161901.pdf
./May-June-2011/161901.pdf
./Nov-Dec-2011/161901.pdf
./Summer-2012/161901.pdf
./Summer-2013/161901.pdf
./Summer-2014/161901.pdf
./Winter-2012/161901.pdf
./Winter-2013/161901.pdf
./Winter-2014/161901.pdf

Eu quero que ./May-June-2011/161901.pdf seja movido para xyz com o novo nome May-June-2011.pdf (nome do diretório dentro do arquivo). ou seja, mova ./May-June-2011/161901.pdf para ./xyz/May-June-2011.pdf

Similarmente, ./Nov-Dec-2011/161901.pdf a ./xyz/Nov-Dec-2011.pdf , ./Summer-2012/161901.pdf a ./xyz/Summer-2012.pdf e assim por diante (até ./Winter-2014/161901.pdf a ./xyz/Winter-2014.pdf ).

A saída esperada para ls xyz é:

$ls xyz
May-June-2011.pdf
Nov-Dec-2011.pdf
Summer-2012.pdf
Summer-2013.pdf
Summer-2014.pdf
Winter-2012.pdf
Winter-2013.pdf
Winter-2014.pdf

Como posso conseguir isso? (como find -exec ou com loop ou outra coisa)

    
por Pandya 29.04.2015 / 07:50

7 respostas

0

Mais tarde, achei while read -r line muito útil e consegui o seguinte comando: -

find -name 161901.pdf | while read -r line; do mv $line ./xyz/$(echo $line | cut -d "/" -f 2).pdf; done

Explicação: -

  • Aqui find -name 161901.pdf lista os arquivos fundados (já mencionados em questão) que canalizaram para while loop enquanto caminho armazenado na variável line .
  • cut -d "/" -f 2 filtra o nome do diretório, por exemplo, May-June-2011
  • E finalmente mv $line ./xyz/$(echo $line | cut -d "/" -f 2).pdf move arquivos (com o nome do diretório renomeado em que eles estavam)

Portanto, find + while loop com mv usando cut para nomeação realiza o que eu quero.

    
por 29.04.2015 / 15:04
4

O módulo Perl Unicode :: Tussle vem com um script muito útil chamado renomear (o que é lamentável, porque o nome se choca com o padrão rename(1) no Linux). Com isso, você poderia fazer algo assim:

mkdir xyz
find . -name '*.pdf' -print0 | \
    rename -0 's!^\.!xyz!;  s!/[^/]*\.pdf$!.pdf!'

Sem Perl, você ainda pode fazer o mesmo com um pouco de encanamento de conchas:

mkdir xyz
find . -name '*.pdf' | sed 's/^/mv /' >src
find . -name '*.pdf' | sed 's!^\./!xyz/!; s!/[^/]*\.pdf$!.pdf!' >dest
join src dest | sh -
rm -f src dest

E, como tenho certeza que outras pessoas serão rápidas em apontar, você pode até colocá-lo em uma única linha, usando <(...) substituição de processo. Não tenho certeza se isso tornaria as coisas mais legíveis. Computadores nos dias de hoje são poderosos o suficiente para permitir uma sintaxe mais detalhada. ;)

    
por 29.04.2015 / 08:32
2

find raramente é útil quando você não precisa percorrer uma árvore de diretórios recursivamente. Aqui, um simples loop e curingas são suficientes.

for x in */161901.pdf; do
  mv -- "$x" "xyz/${x%/*}.pdf"
done

Ou, com o comando rename baseado em Perl no Debian, Ubuntu e derivados ( prename no Arch, não no comando rename do Linux em outras distribuições):

rename 's!/.*!!; $_="xyz/$_.pdf"' */161901.pdf

Ou, com zsh:

autoload -U zmv
zmv '(*)/161901.pdf' 'xyz/$1.pdf'
    
por 03.05.2015 / 00:45
1

Uma maneira é usar find -print e canalizar a lista de arquivos para awk , onde você pode transformá-la para o que quiser, por exemplo, um comando cp :

$ find -name 161901.pdf -print | awk -v TARGET=xyz -F'/' '{ printf "cp %s %s/%s.pdf\n", $0, TARGET, $2; }'
cp ./May-June-2011/161901.pdf xyz/May-June-2011.pdf
cp ./Nov-Dec-2011/161901.pdf xyz/Nov-Dec-2011.pdf

e isso pode ser canalizado para um shell para ser executado:

find -name 161901.pdf -print | awk -v TARGET=xyz -F'/' '{ printf "cp %s %s/%s.pdf\n", $0, TARGET, $2; }' | sh 
    
por 29.04.2015 / 08:33
0

Doce e simples:

#!/bin/bash

find . -name 161901.pdf | while read -r line
do 
  # this will create new filenames e.g. ./May-2013/161901.pdf will be now May-2013-161901.pdf
  filename=$(echo $line | grep -oP "\/\K.+$" | sed 's/\//-/g')
  # now just move the files
  mv $line xyz/$filename
done

Então, basicamente, é apenas um processo de duas etapas. Crie novos nomes de arquivos e mova-os.

    
por 29.04.2015 / 08:34
0

Se você tiver o comando rename / prename

find -name 161901.pdf -exec prename -n 's!/!-!g; s!\W*!xyz/!' {} +

e remova -n se gostar do que vê.

(o nome do príncipe de Larry Wall e Robin Barker é instalado com o pacote perl.deb; Brian d Foy Unicode::Tussle rename também funciona)

    
por 29.04.2015 / 11:19
0

Esta poderia ser uma solução possível. O argumento do script é o diretório em que você está pesquisando e agora está apenas imprimindo o que fará:

#!/bin/bash

find "$1" -type f -name '161901.pdf' -print0 | while IFS= read -r -d '' f
do
    # get folder name
    d="$(basename "${f%/*}")"
    target="${d}.pdf"
    echo cp $f "xyz/${target}"
done
    
por 29.04.2015 / 08:33