mesmo nome de arquivo em vários diretórios: como atribuí-los com nomes diferentes ao mesmo tempo com apenas um comando?

1

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

    
por ALejandro 15.05.2015 / 21:28

5 respostas

2

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' _ {} +
    
por 15.05.2015 / 21:53
1

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
    
por 15.05.2015 / 21:50
0

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 .

    
por 15.05.2015 / 22:09
0

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.

    
por 15.05.2015 / 22:42
0

Isso é muito fácil de fazer com shell globs:

  1. 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
    

    Explicação

    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.

  2. 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
    

    Explicação

    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.

  3. 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
    

    Explicação

    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.

  4. 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 
    
por 16.05.2015 / 14:34

Tags