Renomear pastas em lote com um único comando bash

Renomear pastas em lote com um único comando bash

Eu tenho pastas configuradas assim:

/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3

SLUG_2 é um ano e pode ter uma letra após o ano, como “1994” ou “2003a”.

Gostaria de renomear esses arquivos para:

/path/to/directory/SLUG_1/SLUG_2 - SLUG_3

Estou chegando bem perto com este comando:

find $root -mindepth 2 -maxdeth 2 -type d | sed "s#\(\(/path/to/directory/[^/]*/\).* - \([0-9]\{4\}[a-bA-B]\? - .*\)\)#mv "\1" "\2\3"#

Isso imprime:

mv "/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3" "/path/to/directory/SLUG_1/SLUG_2 - SLUG_3"

Qual é exatamente o comando que desejo executar. Mas não consigo executá-lo.

Tentei atribuir a saída a uma variável e executá-la chamando a variável. Isso não funcionou. Tentei algumas variações dessa ideia e obtive erros.

Parece que estou faltando alguma ferramenta aqui. Alguma ferramenta de iteração que facilita esse trabalho. Alguma sugestão?

Responder1

Supondo que todos os subdiretórios /path/to/directorysigam essa convenção de nomenclatura:

cd /path/to/directory
prename -n 's~/\d{4}[a-z]? - ~/~i' */*

prenameé oPerl renomear(qualquer uma das variantes servirá).

Responder2

Isso imprime:

mv ...

Qual é exatamente o comando que desejo executar. Mas não consigo executá-lo.

Tentei atribuir a saída a uma variável e executá-la chamando a variável. Isso não funcionou. Tentei algumas variações dessa ideia e obtive erros.

Parece que estou faltando alguma ferramenta aqui.

A maneira fácil que você procuraé anexar | bashao seu comando. É assim que você pode passar de "um comando que imprime um comando" para "realmente executar o comando que foi impresso".

No entanto, mesmo que você tenha incluído aspas duplas no comando a ser impresso,isto é umpéssima ideiapara incluir em um script.

Você pode ter lidado com o aspecto do espaço em branco, mas e se os nomes dos arquivos incluírem aspas duplas diretamente? Ou novas linhas? Ou nomes de variáveis ​​(que serão expandidos entre aspas duplas)?

Oapenascaracteres ilegais em nomes de arquivos são uma barra ( /) e um byte nulo.

Ao escrever scripts, você deve manter um padrão de robustez muito mais alto do que em uma linha de comando interativa.

Em uma linha de comando, você pode ver o comando que está prestes a executar, confirmar se está correto e executá-lo. Em um script, não há supervisão nem confirmação. O script fará o que você mandou, não importa o quão destrutivo isso possa ser.

Então ocorretoabordagem é realmente usar find, conforme detalhado em meuoutra resposta. Isso vai lidarqualquernomes de arquivos corretamente e não contém vulnerabilidades de execução de código arbitrário.

Responder3

Outra resposta, usando GNU findpara lidar com a renomeação. Este método é robusto, independentemente dos caracteres que possam estar no nome do arquivo.

Se bem entendi seu caso de uso, você deseja renomear diretórios que começam com o nome completo do diretório pai. Em outras palavras, se o seu diretório tiver o seguinte nome:

/some/path/abcdefghi/abcdefghi - something - else/

Você deseja renomeá-lo assim:

/some/path/abcdefghi/something - else/

Como você especifica GNU como uma tag nesta questão, você pode usar as extensões GNU para o findcomando e lidar com isso da seguinte forma:

find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;

Resultado dos testes:

[vagrant@localhost test]$ mkdir -p SLUG_1/SLUG_{1\ -\ SLUG_{2..4}\ -\ SLUG_5,7\ -\ something}
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_1 - SLUG_2 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_4 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_3 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_3 - SLUG_5
./SLUG_1/SLUG_4 - SLUG_5
./SLUG_1/SLUG_2 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ 

informação relacionada