8

I have the problem described in this Q&A. Probably from quite old linux distros or from windows I have several files with broken filenames. ls displays a "?" instead of the broken character. I successfully renamed some of these files, but I don't know if I've found all of them.

Is there any method to find all affected files?

1

4 Answers 4

11

Assuming you are using utf-8 encoding (the default in Ubuntu), this script should hopefully identify the filenames and rename them for you.

It works by using find with C-encoding (ASCII) to locate files with unprintable characters in them. It then tries to determine if these unprintable characters are utf-8 characters or not. If not, it shows you the filenames decoded with each of the encodings listed in the enc array, allowing you to select the one that looks right in order to rename it.

latin1 was commonly used on older Linux systems, and windows-1252 is commonly used by windows nowadays (I think). iconv -l will show you a list of possible encodings.

#!/bin/bash

# List of encodings to try. (max 10)
enc=( latin1 windows-1252 )

while IFS= read -rd '' file <&3; do
    base=${file##*/} dir=${file%/*}

    # if converting from utf8 to utf8 succeeds, we'll assume the filename is ok.
    iconv -f utf8 <<< "$base" >/dev/null 2>&1 && continue

    # display the filename converted from each enc to utf8
    printf 'In %s:\n' "$dir/"
    for i in "${!enc[@]}"; do
        name=$(iconv -f "${enc[i]}" <<< "$base")
        printf '%2d - %-12s: %s\n' "$i" "${enc[i]}" "$name"
    done
    printf ' s - Skip\n'

    while true; do
        read -p "? " -n1 ans
        printf '\n'
        if [[ $ans = [0-9] && ${enc[ans]} ]]; then
            name=$(iconv -f "${enc[ans]}" <<< "$base")
            mv -iv "$file" "$dir/$name"
            break
        elif [[ $ans = [Ss] ]]; then
            break
        fi
    done
done 3< <(LC_ALL=C find . -depth -name "*[![:print:][:space:]]*" -print0)
2
  • This is really great, I hadn't even realised that this might be the problem, I thought my terminal was just printing badly...
    – naught101
    May 25, 2015 at 12:07
  • Great skript! Runs with uconv instead of iconv also. (uconv is available on Synology DSM OS, iconv is not)
    – alfonx
    Jan 2, 2019 at 15:29
2

Try this:

find / | grep -P "[\x80-\xFF]"

This will locate all non-ASCII characters in file and folder names, and help you to find the guilty culprits :P

1
  • to use find to just find those specific files of interest vs grepping all files: LC_ALL=C find . -name '*[! -~]*' or LC_ALL=C find . -name '*[![:print:]]*' | cat ( see also unix.stackexchange.com/a/109753/7832 )
    – michael
    Aug 10, 2021 at 8:39
1

Begin with this regex find command and modify it until you hit only those you are interested in: find . | egrep [^a-zA-Z0-9_./-\s].
The one above will find filenames that has a non UTF-8 character(s).

8
  • The above did not work for me... Try this instead if it doesn't work for you either: find / | grep -P "[\x80-\xFF]". This will locate all non-ASCII characters in file and folder names.
    – SirCharlo
    Mar 15, 2012 at 14:31
  • @SirCharlo Thanks, this seems to be at least partly successful! But it finds also a lot of filenames which doesn't seem to be affected at first glance.
    – lumbric
    Mar 15, 2012 at 16:50
  • @yossile looks like a promising approach, but I stopped trying after having a very long list of excluded symbols.
    – lumbric
    Mar 15, 2012 at 16:50
  • @lumbric No problem! Could you give me an example of a seemingly irrelevant file name?
    – SirCharlo
    Mar 15, 2012 at 17:28
  • @SirCharlo Sorry, I was wrong. It finds also those filenames with ASCI 7-bit characters - also those where the encoding is correct. But this is exactly what you claimed. I just filtered this output with another |egrep -v [äöüÄÖÜß] (all non-ASCII 7bit characters are german umlauts). Don't you want to convert your comment to an answer, since it was quite helpful too?
    – lumbric
    Mar 16, 2012 at 17:17
0

Here's another version, extremely simple, that uses unidecode utility (install: sudo apt install python3-unidecode), which attempts as best it can to convert Unicode characters to "equivalent" ASCII character. Obviously this comes with caveats; see https://pypi.org/project/Unidecode/

It first renames directories, then files (changing directory names and files at the same time might have unpredictable results, doing depth-first is necessary in the case of renaming directories.)

(With a few minor changes you can make it just print out what would be changed, instead of doing the actual changes.)

#!/bin/bash

[ ! -d "$1" ] && echo "** error: expecting directory to search" && exit 2
DIR=$1

do_rename() {
  orig=$1
  new=$(unidecode -c "$1")

  printf "mv -i \"%s\" \"%s\"\n" "$orig" "$new" ## DEBUG
  mv -i "$orig" "$new"
}
export -f do_rename

# directories
LC_ALL=C find $DIR -depth -type d -name '*[! -~]*' -exec bash -c 'do_rename "$0"' {} \;

# files
LC_ALL=C find $DIR -depth -type f -name '*[! -~]*' -exec bash -c 'do_rename "$0"' {} \;

You must log in to answer this question.

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