4

How do I do a search for any of the following:

Find folders that start with a space on linux and then remove any starting spaces so the folder can start with a letter or a number?

Find FILES that start with a space on linux and then remove any starting spaces so the files can start with a letter or a number?

1
  • 1
    What should be done with names consisting entirely of spaces? Things like touch ' '?
    – terdon
    Sep 25 at 11:39

3 Answers 3

7
find . -depth -name ' *' -exec sh -c '
    for pathname do
        newname=${pathname##*/}
        newname=${newname#"${newname%%[! ]*}"}

        newpathname=${pathname%/*}/$newname

        if [ -z "$newname" ] || [ -e "$newpathname" ]; then
            continue
        fi

        mv -v "$pathname" "$newpathname"
    done' sh {} +

The above finds any file or directory whose name starts with at least one space character in or below the current directory. It does this in a depth-first order (due to -depth) to avoid renaming directories that it hasn't yet processed the contents of; renaming a directory would otherwise cause find not to find it later, as it was renamed.

A short in-line shell script is called for batches of names that start with a space. The script iterates over the given pathnames and starts by extracting the actual name from the end of the current pathname into the variable newname. It does this using the standard parameter substitution, ${pathname##*/}, removing everything to the last / in the string (the longest prefix matching */), leaving the final pathname component. This is essentially the same as "$(basename "$pathname")" in this case.

We then need to trim off the spaces guaranteed to exist at the start of the string in $newname. We do this by first removing everything but the spaces with ${newname%%[! ]*} (the longest suffix matching [! ]*, i.e. from the first non-space character onwards), and then removing the result of that from the start of the $newname string with ${newname#"${newname%%[! ]*}"}.

The destination path in the mv command is made up of the directory path of $pathname concatenated by a slash and the new name, i.e. ${pathname%/*}/$newname, which is essentially the same as "$(dirname "$pathname")/$newname".

The code detects name collisions and silently skips the processing of names that would collide. It also skips names that collapse to empty strings. This is what the if statement before mv does. If you want to bring these names to the user's attention, then do so before continue.

Test run on a copy of your backed-up data.

Test-running the code above:

$ tree -Q
"."
|-- " dir0"
|   |-- "   o   dir1"
|   |   |-- "  otherfile"
|   |   `-- "dir2"
|   |       |-- "  dir3"
|   |       `-- " moar"
|   `-- "file"
`-- "script"

4 directories, 4 files
$ sh script
./ dir0/   o   dir1/dir2/ moar -> ./ dir0/   o   dir1/dir2/moar
./ dir0/   o   dir1/dir2/  dir3 -> ./ dir0/   o   dir1/dir2/dir3
./ dir0/   o   dir1/  otherfile -> ./ dir0/   o   dir1/otherfile
./ dir0/   o   dir1 -> ./ dir0/o   dir1
./ dir0 -> ./dir0

Notice how it starts at the bottom of the directory hierarchy. If it had tried renaming dir0 first, it would have failed to enter it later to rename the other directories and files.

$ tree -Q
"."
|-- "dir0"
|   |-- "file"
|   `-- "o   dir1"
|       |-- "dir2"
|       |   |-- "dir3"
|       |   `-- "moar"
|       `-- "otherfile"
`-- "script"

4 directories, 4 files
4
  • Amazing answer, I am very grateful for taking the time to teach mem all of this, it is much more than I needed.
    – JustAl
    Sep 26 at 0:02
  • When i try this I get "Find: missing argument to '-exec'", when i added a ; at the very end of the script i get $'\r': command not found. @kusalananda
    – JustAl
    Sep 26 at 2:33
  • @JustAl Are you writing your script on a Windows system? This would happen when the script is saved as a DOS text file. Try converting the script to Unix text format with dos2unix or get your editor to save it without CRLF line endings.
    – Kusalananda
    Sep 26 at 5:13
  • 1
    Thank you very much it worked well after I saved it with LF line endings. I appreciate your time and helpfulness.
    – JustAl
    Sep 26 at 17:55
0

This script will look for files starting with spaces and print out the commands it will run. You have to uncomment the actual mv command for this script to work. It should be run in the directory you want. It will make this change for all files and folders inside the current directory, but not within any subdirectories in the current directory. It is possible you have two files named " 123" and " 123" and your instructions would have it renamed to the same thing.

It is not reversible, although I add a -i to the mv, so it will not overwrite files unless you tell it to. I would take extreme caution before running this script, including making a backup of your system in case it blows up.

#!/bin/bash
# tell read not to word split
IFS=
# find all files starting with space
find ./  -maxdepth 1 -name " *" -print0 | \
 while read -d '' a
 do
 # new filename without leading spaces
 nm=$( echo "$a" | sed 's/^.\/ */.\//')
 # use -i to not overwrite files
 echo mv -i \"$a\" \"$nm\"
 # uncomment the next line to do the actual move
 #mv -i "$a" "$nm"
done
3
  • 2
    You will have to use -depth with find to do a depth-first traversal, or you may try to change the name of directories containing names that you need to change later.
    – Kusalananda
    Sep 24 at 22:01
  • 1
    Your algorithm seems to only modify names in the current directory, due to the way you're using sed. If you only care about the current directory, then you don't really need find.
    – Kusalananda
    Sep 24 at 22:03
  • Thanks @Kusalananda . I chose to set maxdepth to 1, as I'm not really interested in further developing this script.
    – toppk
    Sep 24 at 22:07
0

With zsh:

autoload -Uz zmv
zmv -n '(**/) ##(*)(#qD/)' '$1$2'

(remove -n from dry-run if happy)

1
  • Thank you Stéphane, I will try it.
    – JustAl
    Sep 26 at 17:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.