Remove trailing space from file and folder names

I have a folder that contains many sub folders and files. This tree has to be backed up to an archive system. According to the tech support, one of the archives is failing to back up due to the possibility of trailing spaces on file and folder names. Therefore, I would like to have a script parse through this directory tree and remove trailing spaces from file and folder names. I would like to avoid replacing spaces with underscores and I would like to avoid removing spaces from the middle of the names.

For example, I have many folders that might be named something like this:

12345 Company Name

So assuming the above folder had a trailing space, I would want to remove JUST the trailing space.

Can I get some assistance with a script that could do that?

Thanks!

Can you please tell us what you have attempted so far?

I'm a mediocre script writer so the only thing I've managed to do so far is use a script I found that replaces all spaces with underscores. So my example folder I listed above ends up looking like this:

12345_Company_Name_

I managed to get that script to remove ALL spaces and change the name to this:

12345CompanyName

But I don't want that either.

Please use code tags as required by forum rules!

Not sure I understand what you are asking for. A listing of files in a directory with trailing spaces removed? One or many spaces? A script to rename all those files? In subdirectories as well? How far need the required assistance go?

Hint: you can get quite far using bash 's parameter expansion (here: Remove matching prefix/suffix pattern):

FN="12345 Company Name          "

echo ">${FN%${FN##*[^ ]}}<"
>12345 Company Name<

echo mv \""$FN"\" \"${FN%${FN##*[^ ]}}\"
mv "12345 Company Name          " "12345 Company Name"

RudiC, thank you for the response. My apologies for forgetting the code. Didn't think it was necessary for just folder name examples.

I want to be able to parse through a folder, find all subdirectories and files under that folder that might have a trailing space and then remove that trailing space. So the script would basically be a rename script.

So far, I managed to successfully use a script that either renames the file or folder via replacing spaces with underscores or removing all of the spaces. I want to rename all found files and folders and only remove any trailing spaces.

How far do you get with the hint I gave?

I think this is working for me

find . -iname "* *" -printf '%p\0' | while IFS= read -r -d '' FN; do
	FNnew=`echo "${FN%${FN##*[^ ]}}"`
	echo "$FN" "$FNnew"
	mv "$FN" "${FN%${FN##*[^ ]}}"
done

I am running tests on a test folder structure now. The only thing it doesn't seem to do is remove the trailing space on files that have a dot extension.

for example:

this is a test .txt

my script will not remove the space after the word test. But, I believe this will not impact my larger issue and I can live without removing spaces on files like that. I'm more concerned with outside trailing spaces.

Thank you very much for the help. Any other tips are appreciated.

---------- Post updated at 02:27 PM ---------- Previous update was at 02:15 PM ----------

Actually, I have discovered a problem with this. If a folder containing a trailing space is fixed and renamed, any files with trailing spaces found under this folder are not fixed because the folder name is no longer found.

Assuming you are happy with your code so far...
A longhand line by line removal of the trailing space.
OSX 10.7.5, deafult bash terminal...

Last login: Wed Aug 13 19:23:39 on ttys000
AMIGA:barrywalker~> text=".txt"
AMIGA:barrywalker~> txt="This is a line with a trailing space "
AMIGA:barrywalker~> txt="${txt% }"
AMIGA:barrywalker~> txt=$txt$text
AMIGA:barrywalker~> echo "$txt"
This is a line with a trailing space.txt
AMIGA:barrywalker~> _

You need to run find again to get at the new folder name.

So does it make sense then for me to run the script with loop that first goes and changes all of the folder names and then later in the script another loop that then goes through and changes all of the files? Something like this:


find . -type d -iname "* *" -printf '%p\0' | while IFS= read -r -d '' FN;
do
        FNnew=`echo "${FN%${FN##*[^ ]}}"`
        echo "$FN" "$FNnew"
        mv "$FN" "${FN%${FN##*[^ ]}}"
done

find . -type f -iname "* *" -printf '%p\0' | while IFS= read -r -d '' FN;
do
        FNnew=`echo "${FN%${FN##*[^ ]}}"`
        echo "$FN" "$FNnew"
        mv "$FN" "${FN%${FN##*[^ ]}}"
done

You could do the files first and then the directories, like so:

{
  find . ! -type d -name "* " ......
  find . -type d -name "* " ......
} |
while  ...
do 
  ....
done

--
@RudiC, vipertech:
Also note that it is best to use quotes around the inner expansion so that if it contains characters that are special to the outer expansion they will not be interpreted as such:

mv "$FN" "${FN%"${FN##*[^ ]}"}"
1 Like

My two loop idea doesn't work anyways because it still will not find sub-folders of sub-folders already renamed.

Try find -depth, which will process folder contents before the folder itself. This should avoid cutting off the branch you are standing on, so to speak.

You could try find with the -depth option

Thank you everyone for your replies and assistance. Here is a code that I ended up with that works quite well. It is a combination of information I learned here and some scripts I found scattered on the net.

IFS='
'
j=`find $1 -printf "%d\n" | sort -u | tail -n 1`
echo "Max dir depth:" $j
for (( i=0; i<=j ; i++ ));
do
	find . -mindepth $i -maxdepth $i -iname "* *" -printf '%p\0' | while IFS= read -r -d '' FN;
	do
		FNnew=`echo "${FN%${FN##*[^ ]}}"`
		echo "$FN" "$FNnew"
		mv "$FN" "${FN%${FN##*[^ ]}}"
	done
done

It works quite well and I understand what is going on. I do however have some questions about the first line. Could someone brief me on exactly what the

IFS='
'

line is doing? I have seen that line used in other scripts and I'm not 100% certain what its function is. I believe it has been written differently in other scripts as well. Like this:

IFS='\n'

Taken from man bash :

The two forms that you post are equivalent, setting IFS to the <LF> char. But it escapes me to what avail in your code snippet...

RudiC, thanks. I took that code from somewhere on the net and just left that line in. I guess in this particular case, it is unnecessary.

---------- Post updated at 01:25 PM ---------- Previous update was at 11:06 AM ----------

Update to my script:

This line did not calculate the tree depth properly:

j=`find $1 -printf "%d\n" | sort -u | tail -n 1`

I updated it to this and it works

find . -printf '%d\n' | sort -n  | tail -1