Com bash
e find
find . -type f -name accepted_hits.bam -exec bash -c \
'i=0; for f; do (( ++i )); mv -- "$f" "${f}_$i"; done' _ {} +
Eu acho que o título é auto-explicativo, mas meu problema é: Eu tenho 23 diretórios diferentes e em cada um deles eu tenho um arquivo chamado accept_hits.bam
Por isso, preciso gerar um nome exclusivo em cada diretório: por exemplo, accepted_hits.bam_1 para o diretório 1 accepted_hits.bam_2 para o diretório 2
e assim por diante 23 vezes. Existe uma maneira fácil de fazer isso com um único comando? ALe
Com bash
e find
find . -type f -name accepted_hits.bam -exec bash -c \
'i=0; for f; do (( ++i )); mv -- "$f" "${f}_$i"; done' _ {} +
Supondo que seus diretórios sejam todos do mesmo pai, tente isto:
$ for f in $(ls */accepted_hits.bam); do mv $f $(dirname $f)/$(dirname $f)-$(basename $f); done
Eu provavelmente usaria awk
, mas isso é apenas uma questão de gosto, não de certo e errado:
find . -name accepted_hits.bam | awk '{ print "cp -pi \"" $0 "\" \"" $0 "\"_" NR "; mv -i \"" $0 "\"_" NR " ./bam-files/" }' | tcsh -f
onde você precisa criar um diretório ./bam-files/
antes de chamar a linha acima ou ajustar o nome do diretório. Eu intencionalmente usei -p
para preservar tempos de arquivos e -i
para evitar substituições, apenas no caso. Então, antes de chamar os comandos pela segunda vez, limpe o diretório de destino.
Se você deseja remover os arquivos originais, substitua o cp -pi
por mv -i
.
O script deixa de fora chamadas para basename
na esperança de ter menos problemas se o nome do caminho contiver caracteres especiais (espaços e a maioria dos caracteres especiais são manipulados).
PS: Você também pode fazer uma execução a seco antes de executar, deixando de fora o | tcsh -cf
no final. Se você não tem tcsh
instalado (sim, existem Linuxes sem ele) sinta-se à vontade para usar sh
ou bash
.
Se quiser apenas nomeá-los como arquivos exclusivos, você pode usar a variável RANDOM
conforme abaixo:
find /basedir/ -type f -name "accepted_hits.bam" -exec bash -c 'mv $0 $0.$RANDOM' {} +
Se você gostaria de nomeá-los sequencialmente, você pode anexar um valor de contador no final do nome do arquivo, conforme abaixo:
for file in $(find /basedir/ -type f -name "accepted_hits.bam"); do ((count++)) ; mv $file ${file}_${count}; done
Os dois métodos acima renomeiam o arquivo no mesmo diretório. Se você gostaria de renomeá-los para um diretório diferente, você pode combiná-los com a resposta de @ schlimmchen (usando basename e dirname). Você pode até mesmo inserir uma instrução echo antes do comando move para ver o resultado sem realmente mover os arquivos. Descobri que é uma maneira prática de testar os comandos sem arriscar nenhuma alteração nos arquivos.
Isso é muito fácil de fazer com shell globs:
Adicione um contador ao nome ( accepted_hits.bam.1
, accepted_hits.bam.2
e assim por diante):
c=0; for f in */accepted_hits.bam; do mv "$f" "$f".$((++c)); done
Isso simplesmente itera todos os arquivos chamados accepted_hits.bam
que estão nos subdiretórios do diretório atual e salva cada um deles como $f
. A construção $((++c))
incrementa um contador ( $c
), então $f.$((++c))
será file.1
, file.2
etc, incrementado em cada iteração.
Observe que estou usando c=0
no começo. Se você não fizer isso, as execuções subsequentes manterão o valor de $c
e continuarão incrementando.
Adicione um contador, mas antes da extensão ( accepted_hits.1.bam
, accepted_hits.2.bam
e assim por diante):
c=0; for f in */accepted_hits.bam; do
mv "$f" "$(dirname "$f")"/"$(basename "$f" .bam)".$((++c)).bam
done
O comando dirname
retorna o caminho para o diretório pai de um arquivo:
$ dirname /usr/local/bin/apt
/usr/local/bin
O comando basename
é o inverso, ele remove o diretório, deixando apenas a última parte do caminho e também pode remover uma extensão:
$ basename /etc/apt/sources.list
sources.list
$ basename /etc/apt/sources.list .list ## remove extension
sources
Portanto, mv "$f" "$(dirname "$f")"/"$(basename "$f" .bam)".$((++c)).bam
irá renomear o arquivo conforme desejado.
Adicione o nome do diretório pai ( accepted_hits.dir1.bam
, accepted_hits.dir2.bam
ou qualquer nome de diretório)
for f in */accepted_hits.bam; do
dir="$(dirname "$f")"; mv "$f" "$dir"/"$(basename "$f" .bam)"."$dir".bam
done
Esta é a mesma ideia acima, apenas estou salvando o nome do diretório como uma variável para simplificar o uso como parte do nome do arquivo.
Use ferramentas externas. Perl rename
por exemplo. O rename
padrão que vem com as distribuições baseadas no Debian não pode lidar com incremento de contadores. No entanto, eu encontrei este que pode. Faça o download do script vinculado a essa página, salve-o como increname
em seu diretório atual e execute-o assim:
perl increname -n 's/.bam/++$c . ".bam"/e' */accepted_hits.bam
Tags rename