Recursive file renamer - very useful python script stops working after upgrade Python (Pyt2 to Pyt3). Can anyone find and fix the problem please? Thank you!

Example, those files:

'Linux příručka českého uživatele.pdf'
'LINUXTERO příkazy které se vždy hodí.pdf'
'Práce s archívy příkazové řádka.pdf'
'Rekurzivní grep.txt'

change to those files:


Programmer is Czech, so I have to translate via Google to English...

When I start the script I got failed massage as follows:

linq@legion5:~/test-renamer/ask_stackexchange_folder$ vycisti.py
File "/home/linq/bin/vycisti.py", line 62
print "Warning: '%s' is still not valid name" % fileName
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

Here is the script:

# -*- coding:  utf-8

'''Renames all files and directories in the current directory to
    to have some culture. It also works recursively in nested ones
     directories. The changes are as follows:
     Czech characters -> English
     cancels all dashes and spaces at the beginning of the name (damn, what kind
             the names make up would deserve a few slaps)
     special characters are replaced by '_', I take the hyphen at mercy, but not at the beginning
     the (unnecessary) '_' around the hyphen is discarded
     multiple '_'s are replaced by a single occurrence
     everything will be converted to lowercase

     The result should be a title where there are only letters, underscores,
     numbers, periods and dashes (but not at the beginning).
     It writes exactly what it does to standard output
     Usage: vycisti.py [ > logfile ]

import re
import os
import sys
import unicodedata

class RenameFiles:
    def __init__(self):
        if os.environ.get('OS','') == 'Windows_NT':
            self.locale = "cp1250"
        else: #probably linux
            local = os.environ.get('LANG', '')
            if '.' in local: #Fedora way
                self.locale = local.split('.')[1]
            else: #Debian
                self.locale = 'iso-8859-2'

    def cleanString(self, what):
        '''Gets rid of letters which are not in English alphabet'''
        assert type(what) == unicode
        normalized = unicodedata.normalize('NFKD', what)
        output = ''
        for c in normalized:
            if not unicodedata.combining(c):
                output += c
        return output

    def cleanName(self, fileName):
        '''Convert the givne string into a form which is suitable for a file name'''
        assert type(fileName) == str
        fileName = self.cleanString(fileName.decode(self.locale))
        fileName = re.sub("^[-\ ]+", "", fileName) #delete space or dash at the beginning
        invalid_stuff = re.compile(r"[^a-zA-Z0-9_\.-]+") #forbidden characters (non-alfanumerical)
        fileName = invalid_stuff.sub("_", fileName.strip()) #replace invalid stuff and spaces by _,
        fileName = re.sub("_+", "_", fileName) #squeeze continuous underscores to one _
        fileName = re.sub("-+", "-", fileName) #squeeze continuous dashes to one _
        fileName = re.sub("_*-_*", "-", fileName) #removes useless '_' round the dash
        fileName = re.sub("_*\._*", ".", fileName) #removes useless '_' round the dot
        fileName = re.sub("-*\.-*", ".", fileName) #removes useless '-' round the dot
        fileName = fileName.lower() #lower case
        valid_name=re.compile(r"^[a-z0-9_\.][a-z0-9_\.-]+$") #regular expression for feasible name
        if not valid_name.match(fileName):
            print "Warning: '%s' is still not valid name" % fileName
        return fileName.encode(self.locale)

    def renameFile(self, dir, fileName):
        '''Public: Renames the file fileName in the directory'''
        assert type(fileName) == str
        assert type(dir) == str
            new = self.cleanName(fileName)
            print "Problem: %s %s " % (fileName, sys.exc_info()[0] )
            new = ""
        if (new != "" and new != fileName):
            print "Renaming %s: %s -> %s" % (dir, fileName, new) #kontrolní výpis
            os.rename(dir+os.sep+fileName, dir+os.sep+'tmp')
            os.rename(dir+os.sep+'tmp', dir+os.sep+new)

    def process_dir(self, dir):
        """process all files in the folder"""
        assert type(dir) == str
        for f in os.listdir(dir):
            fileName = dir + os.sep + f
            if os.path.isdir(fileName) and os.access(fileName, os.W_OK): #if it is directory
                self.process_dir(fileName) #process the elements in the directory
                self.renameFile(dir, f) #rename the directory itself
                self.renameFile(dir, f) #if it is a file

if __name__=='__main__':
        renamer = RenameFiles()

  • 1
    Python3 is not compatible with python2, scripts written for python2 must be run using python2, not python3. Please edit your question and add i) your operating system ii) the exact command you use to launch the script. You should still have a python2 available on your system, you just need to use that one to run this script. If you tell us more about your operating system we can help you find it.
    – terdon
    Jan 8 at 19:13
  • @terdon python 2 is officially EOL since 2020. We will start to see systems where python 2 is no longer "available" if that day is not already here. Jan 8 at 19:37
  • @PhilipCouling Not sure what you mean. I said the OP should still have a python2 available, not that they absolutely do. Maybe they don't, but that would be surprising since most systems, as far as I know, still offer both. If the OP is on a system that doesn't, then we'll deal with that then, but that's why I asked.
    – terdon
    Jan 8 at 19:41
  • I was just cautioning against that assumption. With python 2 was sunset in 2020, compatibility will fail over time. Guessing lib C will be the first culprit. But my point is that without knowing the distribution it's harder to be sure EOL products will be available. Jan 8 at 19:49
  • Added 2 new ways in my answer. I assume you use Python only because 'it works' Jan 8 at 20:06

2 Answers 2


As said in comments, Python2 is deprecated. There's another tools than can do the trick...

Using Perl's rename:

$ rename -u utf8 -n  '
    BEGIN{use Text::Unidecode} # 'import' needed extra module
    s/.*/unidecode($&)/e;      # decode utf8 -> ascii
    s/\s+/_/g;                 # s/[[:space:]]/_/g
    y/A-Z/a-z/                 # translate uppercase to lower
' ./**/*.*                     # ** enable recursion¹

¹ zsh default, bash need shopt -s globstar before the command to run.

Need sudo apt install rename libtext-unidecode-perl packages (Debian*/Ubuntu/Mint...).

Remove -n (aka dry-run) when the output looks satisfactory.


rename(Linux příručka českého uživatele.pdf, linux_prirucka_ceskeho_uzivatele.pdf)
rename(LINUXTERO příkazy které se vždy hodí.pdf, linuxtero_prikazy_ktere_se_vzdy_hodi.pdf)
rename(Práce s archívy příkazové řádka.pdf, prace_s_archivy_prikazove_radka.pdf)
rename(Rekurzivní grep.txt, rekurzivni_grep.txt)
  • Thank you very much! Now I have to find out how works this command.
    – linq
    Jan 9 at 0:00

Python 2 is now a dead language (sunset jan 1 2020)

There was a long time where people wrote code compatible with both python 2 and 3. So they are similar, but differences remain.

Perhaps the most well known is the one mentioned in the error. "print" statements now need brackets:

print "foo"

Is now


  • Thank you very much! I will not use it in the future. I better try Perl as Gilles Quenot advise. I am using Linux Mint for long time, and its script works flawlessly until sys upgrade.
    – linq
    Jan 8 at 23:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .