I have hundreds of files which look similar to:
"QDN34 Unit5 mark-up - Judy .pdf"
"QDN34 Unit7 mark-up - Judy .pdf"
"file with two character ext .ai"
"file with dot. trailing space and no ext "
"file with no ext"
Notice that there is a space at the end of all but the last, excluding file extensions where relevant.
I need to retain spaces within the filenames (not ideal) and remove those trailing spaces.
The result should be:
"QDN34 Unit5 mark-up - Judy.pdf"
"QDN34 Unit7 mark-up - Judy.pdf"
"file with two character ext.ai"
"file with dot. trailing space and no ext"
"file with no ext"
So far I have:
newName=$(find . -type f -name \*\ .* | cut -d'.' -f2 | sed 's/\ $//' | sed 's/^\/*//')
extens=$(find . -type f -name \*\ .* | sed 's@.*/.*\.@.@')
oldName=$(find . -type f -iname \*\ .* | sed 's/^\.\/*//')
for f in "$oldName" ; do mv -nv "$oldName" "$newName""$extens" ; done
But I am getting errors which look like the indexes are not matching. It feels like I should be using an array, but I'm not sure how.
The output was:
mv: rename file with two character ext .ai
QDN34 Unit5 mark-up - Judy .pdf
QDN34 Unit7 mark-up - Judy .pdf to file with two character ext
QDN34 Unit5 mark-up - Judy
QDN34 Unit7 mark-up - Judy.ai
.pdf
.pdf: No such file or directory
This is one where a Bash solution may be useful as well. The following tests for an extension (max 4 chars + .
) set with extsz
in the script. If an extension is found, the script trims the whitespace from the filename and then moves the file from old to new name (actual move commented below). It relies on parameter expansion/substring replacement to manipulate the whitespace and filenames:
#!/bin/bash
declare -i extsz=-5 # extension w/i last 4 chars
## trim leading/trailing whitespace
function trimws {
[ -z "$1" ] && return 1
local strln="${#1}"
[ "$strln" -lt 2 ] && return 1
local trimstr=$1
trimstr="${trimstr#"${trimstr%%[![:space:]]*}"}" # remove leading whitespace characters
trimstr="${trimstr%"${trimstr##*[![:space:]]}"}" # remove trailing whitespace characters
printf "%s" "$trimstr"
return 0
}
## for each filename read from stdin
while read -r ffname || test -n "$ffname" ; do
## test for extension and set 'ext' if present
for ((i=$extsz; i<0; i++)); do
[ "${ffname:(i):1}" == '.' ] && { ext=${ffname:(i)}; break; }
done
## if extension, move the file to name w/o trailing space w/orig extension
if [ -n "$ext" ]; then
fname="${ffname%.*}" # separate filename from extension
fnwosp="$(trimws "$fname")" # trim whitespace from filename
printf " renaming : '%s' -> '%s'\n" "$ffname" "${fnwosp}${ext}"
#mv "$ffname" "${fnwosp}${ext}" # commented for testing
else
## if no extension, just trim whitespace and move
printf " renaming : '%s' -> '%s'\n" "$ffname" "$(trimws "$ffname")"
# mv "$ffname" "$(trimws "$ffname")"
fi
unset ext # unset 'ext' for next iteration
done
exit 0
Input
$ cat dat/wfname.txt
QDN34 Unit5 mark-up - Judy .pdf
QDN34 Unit7 mark-up - Judy .pdf
file with two character ext .ai
file with dot. trailing space and no ext
file with no ext
Output
$ bash fixfilenames.sh <dat/wfname.txt
renaming : 'QDN34 Unit5 mark-up - Judy .pdf' -> 'QDN34 Unit5 mark-up - Judy.pdf'
renaming : 'QDN34 Unit7 mark-up - Judy .pdf' -> 'QDN34 Unit7 mark-up - Judy.pdf'
renaming : 'file with two character ext .ai' -> 'file with two character ext.ai'
renaming : 'file with dot. trailing space and no ext' -> 'file with dot. trailing space and no ext'
renaming : 'file with no ext' -> 'file with no ext'
Note: when reading from stdin
, the shell will strip trailing spaces for filenames without extensions.
Reading filenames as Arguments
To illustrate removing spaces from the end of filenames without extensions, it is necessary to quote and read the filenames as arguments. If that is what you need, here is a replacement. It probably makes more sense to read the filenames as arguments rather than in bulk from stdin anyway:
#!/bin/bash
declare -i extsz=-5 # extension w/i last 4 chars
## trim leading/trailing whitespace
function trimws {
[ -z "$1" ] && return 1
local strln="${#1}"
[ "$strln" -lt 2 ] && return 1
local trimstr=$1
trimstr="${trimstr#"${trimstr%%[![:space:]]*}"}" # remove leading whitespace characters
trimstr="${trimstr%"${trimstr##*[![:space:]]}"}" # remove trailing whitespace characters
printf "%s" "$trimstr"
return 0
}
## test at least 1 command line argument
[ $# -gt 0 ] || {
printf "error: insufficient input. usage: %s <filename>\n" "${0##*/}"
exit 1
}
## for each of the filenames give as arguments
for ffname in "$@"; do
## test for extension and set 'ext' if present
for ((i=$extsz; i<0; i++)); do
[ "${ffname:(i):1}" == '.' ] && { ext=${ffname:(i)}; break; }
done
## if extension, move the file to name w/o trailing space w/orig extension
if [ -n "$ext" ]; then
fname="${ffname%.*}" # separate filename from extension
fnwosp="$(trimws "$fname")" # trim whitespace from filename
printf " renaming : '%s' -> '%s'\n" "$ffname" "${fnwosp}${ext}"
#mv "$ffname" "${fnwosp}${ext}" # commented for testing
else
## if no extension, just trim whitespace and move
printf " renaming : '%s' -> '%s'\n" "$ffname" "$(trimws "$ffname")"
# mv "$ffname" "$(trimws "$ffname")"
fi
unset ext
done
exit 0
Example
$ bash fixfilenames.sh 'testfile w end space '
renaming : 'testfile w end space ' -> 'testfile w end space'
$ bash fixfilenames.sh 'file with two character ext .ai'
renaming : 'file with two character ext .ai' -> 'file with two character ext.ai'