Reputation: 471
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
Upvotes: 4
Views: 2818
Reputation:
#!/bin/bash
IFS=$(echo -en "\n\b") && for a in $(ls -f1 *); do
file_ext="${a##*.}"
if [[ ! -z "${file_ext}" && ${#file_ext} -lt 4 ]]; then
file_base="${a%.*}"
else
file_base="${a}"
file_ext=""
fi
[ "${file_base:$((${#file_base}-1)):1}" = " " ] && \
file_base="${file_base% *}"
new_file="${file_base}${file_ext:+.}${file_ext}"
if [ ! "${new_file}" = "${a}" ]; then
echo "mv -nv \"$a\" \"${new_file}\""
#mv -nv "${a}" "${file_base}.${file_ext}"
fi
done
You can run this bash script safely in the directory where you want to rename them to see what it would do, then if it will accomplish what you want you can uncomment the real move command and re-run it.
Updated per OP comments - this script considers a .
having to do with a file extension up to 3 (less than 4) from the right hand side, change it to reflect your dataset.
Upvotes: 0
Reputation: 84599
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'
Upvotes: 1
Reputation: 5298
Try this:
find . -type f | while read i; do mv "$i" "$(sed 's/ \.\([a-z0-9]*\)$/\.\1/' <<< "$i")"; done;
For each file (recursively from current directory), remove the space before the file extension (using sed
) and then rename the original file with new name (result of the sed
command).
Upvotes: 0
Reputation: 1503
You may try the prename
utility with a following perl regular expression:
user@host $ prename 's/\s(\..+$)/$1/g' *
\s
- means space character\.
- means point.
- means one character+$
- means repeating the previous symbol up to the end of the line$1
- means replace with expression
in ()
If you don't have this utility you can use the following bash script:
# you can replace the "*" in the line below
# with any necessary find or ls command to rename only necessary files
for old_name in *;
do
new_name=$(echo "$old_name"|sed 's/ \(\.[^ \t]*$\)/\1/g')
echo "\"$old_name\" --> \"$new_name\""
mv "$old_name" "$new_name"
done
Upvotes: 1