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/directory
sigam 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 | bash
ao 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 find
para 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 find
comando 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]$