Aqui, como apontado por @jordanm, seu problema é que você está chamando mv
no nome dos arquivos para cada diretório percorrido por os.walk
, mas os.walk
não altera o diretório de trabalho atual.
Portanto, para os arquivos encontrados em subdiretórios, isso não funcionará.
Você precisaria passar o caminho completo do arquivo para mv
, então algo como os.path.join(dirpath, name)
.
O ideal seria que o pé alterasse os diretórios à medida que ele progride como perl
File::Find
finddepth()
ou BSD / GNU find -execdir
do, o que tornaria mais seguro e evitar problemas com árvores de diretório muito profundas, mas não acho que você possa fazer isso facilmente com python
' os.walk()
.
Agora, há alguns outros problemas com seu código:
Vulnerabilidade de injeção de comando
Agora, espaços à direita são a menor das suas preocupações:
subprocess.call("mv '%s' '%s'"%(name,name.strip()),shell=True)
Isso é basicamente uma vulnerabilidade de injeção de comando (pense, por exemplo, em um arquivo chamado '$(reboot)'
(com as aspas)).
Como regra, não incorpore texto arbitrário em strings interpretadas como código de shell (ou código em qualquer idioma que possa causar algum dano).
Com seu código (a variante que usa o formulário 'mv "%s" "%s"'
), você pode ter o mesmo erro com arquivos chamados randconf $x
ou randconf $(test)
, por exemplo.
Aqui, use:
subprocess.call(("mv", "--", name, name.strip()),shell=False)
Se você tiver que usar um shell, uma maneira melhor de passar dados para esse shell é usar variáveis de ambiente:
os.putenv("OLD", name)
os.putenv("NEW", name.strip())
subprocess.call('mv -- "$OLD" "$NEW"',shell=True)
A execução de uma shell também é cara. Especialmente em sistemas em que sh
é, na verdade, um grande shell completo como bash
, ksh93
ou zsh
que geralmente leva um tempo significativo para carregar e inicializar.
Enquanto você está chamando o shell, é melhor fazer todo o processo e renomear o código do shell.
Ambiguidade de mv
mv
não é um comando com a melhor interface ( cp
e ln
têm os mesmos problemas). O problema é que mv
faz muitas coisas diferentes, mas não com base no que / como você pede, mas no contexto.
mv A B
Ou
- renomeia A para B / A se B existir e for do tipo diretório ou symlink para o diretório no mesmo sistema de arquivos
- faz o mesmo, mas com uma cópia (preservando o máximo de atributos possíveis), seguido por delete se a renomeação cruzar um limite do sistema de arquivos
- faz uma renomeação de outra forma (excluindo o destino se ele existia de antemão)
Aqui, você quer apenas uma chamada básica do sistema rename()
, ou melhor, um rename()
que não atrapalha um arquivo já existente (como o Linux ' renameat2(... RENAME_NOREPLACE)
), que também alivia problemas como "randconf_1 "
e "randconf_1 "
sendo renomeados para randonf_1
.
Com o GNU mv
, você pode fazer isso com:
mv -nT -- "$old" "$new"
mas isso não é portátil. (Observe também que o mv
do GNU não usa renameat2()
no Linux, então tem uma condição de corrida (menor aqui))
Em qualquer caso, em python
, você não precisa chamar um programa separado apenas para renomear um arquivo.
os.rename(name, name.strip());
(não sei que python
tem uma ligação com o Linux ' renameat2()
)
space vs whitespace
python
' strip()
retira os caracteres iniciais e finais espaço em branco . Aqui todas as strings começam com randconf
, então é o mesmo que rstrip()
. espaço em branco inclui o caractere de espaço ASCII, mas todos os tipos de outros caracteres de espaçamento vertical e horizontal (apenas os ASCII pareceriam), como TAB, LF, CR ...
Como você está procurando arquivos que contenham caracteres de espaço em qualquer lugar da linha, você pode acabar não renomeando alguns nomes de arquivos que terminam com espaço em branco (como "randconf_\t"
que não contém espaço) ou chame mv
para nomes de arquivos que não terminam em espaço em branco (como "randconf_x y"
).
Você poderia usar fnmatch.filter(filenames, "randconf* ")
e rstrip(" ")
se apenas se preocupasse com caracteres de espaço à direita
Equivalente ao shell POSIX:
Faça isso com a sintaxe do shell e do utilitário POSIX:
find . -depth -name 'randconf*[[:space:]]' ! -type d -exec sh -c '
for file do
newfile=${file%"${file##*[![:space:]]}"}
[ -e "$newfile" ] || [ -L "$newfile" ] || mv -- "$file" "$newfile"
done' sh {} +
Ou ligeiramente mais confiável com os utilitários GNU
find . -depth -name 'randconf*[[:space:]]' ! -type d -execdir bash -O extglob -c '
for file do
newfile=${file%*([[:space:]])}
mv -nT -- "$file" "$newfile"
done' sh {} +
(note que ele remove todos os caracteres considerados como espaço em branco na localidade atual, não apenas os caracteres ASCII como em python; altere a localidade para C
para corresponder apenas ao espaço em branco ASCII).