Renomeando todos os arquivos terminados com .c presente no diretório recursivo

1

Eu preciso de um shell script que inclua alguma string no nome do arquivo (arquivos .c) presente em um diretório e subdiretório.

Por exemplo: Se lokesh for o diretório pai e dentro dele lokesh1 e lokesh2 é o subdiretório e dentro de lokesh1 ( 1.c , 2.c ...) e dentro de lokesh2 ( a.c , b.c ...). Portanto, depois de executar o script, quero alterá-lo para ( x_1.c , x_2.c ...) e (x_a.c, x_b.c ...).

    
por Lokesh Kumar 06.04.2018 / 15:51

2 respostas

7

NOTA: Ao escrever o abaixo, eu estava usando o OpenBSD find com o qual -execdir utility {} ';' irá substituir {} pelo nome base dos arquivos encontrados. Com% GNUfind, o {} também será o nome base do arquivo encontrado, mas será adicionalmente adicionado por ./ , que renderiza o primeiro comando find abaixo de inútil. Para usuários do GNU find (a maioria das pessoas no Linux, por exemplo), vá até as outras variações da solução.

Você deseja encontrar todos os arquivos .c dentro ou abaixo do diretório lokesh e prefixar seus nomes com x_ .

find lokesh -type f -name '*.c' -execdir echo mv {} x_{} ';'

As expressões -type f e -name '*.c' encontrarão todos os arquivos relevantes, enquanto -execdir mv {} x_{} ';' renomeará os arquivos encontrados.

A expressão -execdir não é padrão, mas a maioria das implementações de find oferece suporte a ela. Ele difere de -exec em que o utilitário fornecido é executado com o diretório pai do caminho encontrado como o diretório de trabalho. O {} na linha de comando será, portanto, o nome de base dos arquivos que queremos renomear (não o nome completo do caminho como -exec ).

Execute uma vez e, em seguida, remova o echo quando tiver visto que ele faz a coisa correta. O echo impedirá que o mv realmente renomeie os arquivos.

Em sistemas que não suportam -execdir (ou que não faz -execdir como o OpenBSD faz):

find lokesh -type f -name '*.c' \
    -exec sh -c 'for name do echo mv "$name" "${name%/*}/x_${name##*/}"; done' sh {} +

ou, mais curto, mas sempre menos eficiente,

find lokesh -type f -name '*.c' \
    -exec sh -c 'echo mv "$1" "${1%/*}/x_${1##*/}"' sh {} ';'

Ambas as variações empregam um script de shell curto que basicamente faz a mesma coisa no final:

mv "$name" "${name%/*}/x_${name##*/}"

Isso move o arquivo com seu nome de caminho completo em $name para um novo nome prefixado por x_ no mesmo diretório. A substituição do parâmetro ${name%/*} é equivalente a $( dirname "$name" ) e fornecerá o diretório pai do nome do caminho, enquanto ${name##*/} é equivalente a $( basename "$name" ) , o que fornecerá o nome da base (o nome do arquivo) do nome do caminho.

    
por 06.04.2018 / 16:02
1

Você pode usar renomear Perl, se disponível. Por renomear Perl, quero dizer tanto nome de site ou renomeação de arquivo, e não rename.ul que pode ser o que rename aponta para o seu sistema, e que definitivamente irá errar se você tentar usá-lo com os argumentos de código que sugeri abaixo ...

No meu sistema (Ubuntu 17.10 - instalei o pacote rename para obter renomeação Perl):

$ readlink -e $(type -P rename)
/usr/bin/file-rename

Este é o único ambiente em que testei esta resposta!

Se você tiver apenas dois níveis de diretório, como no seu exemplo, poderá executar a partir do diretório lokesh algo assim:

rename -n 's|/|/x_|' */*.c

Sempre execute primeiro com o sinalizador -n , que mostra qual alteração será tentada se você executar o mesmo código sem -n . Se você está vendo o resultado correto, remova -n .

Se você tiver uma estrutura de diretório complexa, poderá usar globalização recursiva, se disponível. No Bash:

shopt -s globstar

Em seguida, experimente rename :

rename -n 's|(.*)/|$1/x_|' ./**/*.c

Isso captura tudo antes do último elemento de caminho (.*)/ e o reproduz com a referência de referência $1 .

Caso contrário (se você não pode usar globalização recursiva), use find :

find . -type f -name "*.c" -exec rename -n 's|(.*)/|$1/x_|' {} +

Embora seja mais lento se os arquivos estiverem em vários diretórios, você também pode usar -execdir , o que permite uma regex mais simples (descobri uma diferença de velocidade notável entre -exec (menos de um segundo) e -execdir ( 3-4 segundos) com ~ 1113 .c arquivos em ~ 175 diretórios):

find . -type f -name "*.c" -execdir rename -n 's|/|/x_|' {} +

O -n fornece resultados confusos com -execdir , mas se os nomes das bases parecerem corretos, estamos ok.

No entanto, graças a um comentário por Kusalananda , eu percebo que isso depende de ter o GNU find onde todos os argumentos -execdir começam com ./ , e não a versão do OpenBSD onde eles não. Se o nome do arquivo não tiver um prefixo de caminho, imagino que você poderia usar esta versão:

find . -type f -name "*.c" -execdir rename -n -- 's|^|x_|' {} +

mas não posso testar isso porque não tenho o OpenBSD find .

No entanto, Eliah Kagan muito prestativo que funciona independentemente da implementação de find que você está usando e se usa -exec ou -execdir e também funciona com globalização recursiva:

rename -n 's|[^/]+$|x_$&|'

Isso corresponde a pelo menos 1 caractere (s) que não são / no final ( $ ) do caminho e, em seguida, reproduz a correspondência inteira ( $& ) após o x_ adicionado. Este comando deve funcionar em qualquer lugar find e a renomeação do Perl estão disponíveis:

find . -type f -name "*.c" -exec rename -n 's|[^/]+$|x_$&|' {} +
    
por 06.04.2018 / 18:46