rename fails on filenames with spaces - ksh

I have a KSH simple script that creates a list of files we receive on our FTP server.
sF_Array=(` find . -type f ... `)
sF_len=${#sF_Array[*]}
for name in ${sF_Array[#]}
do
basename "$name" # This removes the leading slashdot
done>$ddw_data/FTP_FILE_LIST
Challenge is that some of the files have spaces in them (Files are coming to us from MS Windows machines) so I used rename
for name in ${sF_Array[#]}
do
rename 's/ /_/g' "$name" # This removes spaces in file names
basename "$name" # This removes the leading slashdot
done>$ddw_data/FTP_FILE_LIST
but it does not to work. This is what FTP_FILE_LIST looks like (notice 2nd and 3rd records/filenames):
TCA~PRE~PREP~9939985~ONTDTVE3A~33~F~3922~R22-100~000806451655~20130819~040320.XML
J
MEM~ETT~CVT~654687-MEMTO~jgm16227~1~P~1100~R24-500~033068658187~20130813~234639.XML
J
MEM~FUN~TEST~312326-MEMTO~jgm16227~2~P~1100~R24-200~035696412416~20130813~234638.XML
J-MEM~DCA~FVT~5333131~ONTDTVD1C~1~F~3420~DECA1MR0-01~XED1B1201A3313~20130827~201916.XML
TCA~COS~COSM~95518585~ONTDTVE7A~63~F~3911~R22-300~00065881346~20130817~000300.XML
I want FTP_FILE_LIST to look like this instead (notice 2nd and 3rd records/filenames):
TCA~PRE~PREP~9939985~ONTDTVE3A~33~F~3922~R22-100~000806451655~20130819~040320.XML
J_MEM~ETT~CVT~654687-MEMTO~jgm16227~1~P~1100~R24-500~033068658187~20130813~234639.XML
J_MEM~FUN~TEST~312326-MEMTO~jgm16227~2~P~1100~R24-200~035696412416~20130813~234638.XML
J-MEM~DCA~FVT~5333131~ONTDTVD1C~1~F~3420~DECA1MR0-01~XED1B1201A3313~20130827~201916.XML
TCA~COS~COSM~95518585~ONTDTVE7A~63~F~3911~R22-300~00065881346~20130817~000300.XML
What am I missing??

rename will rename the actual file, not change the variable. Use tr or something to fix the file names inline:
basename "$(echo "$name" | tr " " "_")"

I resolved my problem by creating two separate tasks in my script:
1) to rename files with spaces
find . -type f \( ...\) | while read file
do
target=`echo "$file" | sed 's/ /_/g'`
mv "$file" "$target"
done
2) to create my file list
sF_Array=(` find . -type f ... `)
sF_len=${#sF_Array[*]}
for name in ${sF_Array[#]}
do
basename "$name" # This removes the leading slashdot
done>$ddw_data/FTP_FILE_LIST
It's interesting that it works this way but does not in one single step.
Thanks for your time.

Related

Renaming multiple directories while keeping a part in the middle with varying suffix

I'm trying to change the name of multiple directories using bash, the names being structures like the following:
DRMAD_CA-12__MRBK01_237a8430 DRMAD_CA-17__MRBK10_766c3396
DRMAD_CA-103__MRBK100_c27a6c1c
The goal is the to keep the MRBK as well as the number following directly after it (MRBK###), but to get rid of of the rest. The pattern of the prefix is always the same (DRMAD_CA-###__), while the suffix is '_' followed by a combination of exactly 8 letters and digits. Tried sed, but can't seem to figure out the right pattern.
Seeing other posts on Stackoverflow, I've tired variations of
ls | while read file; do new=$( echo $file | sed 's/[^0-9]*\([^ ]*\)[^.]*\(\..*\)*MRBK\1\2/' ) mv "$file" "$new" done
But since I don't really understand the syntax of sed, it doesn't produce a usable result.
Use rename utility.
First, print the old and new names, but do not rename anything:
rename --dry-run 's/.*(MRBK\d+).*/$1/' *MRBK*
If OK, actually rename:
rename 's/.*(MRBK\d+).*/$1/' *MRBK*
Install rename, for example, using conda.
Using find:
find . -type d -regextype posix-extended -regex "^.*MRBK[[:digit:]]+.*$" | while read line
do
dir=$(dirname $line)
newfil=$(grep -Eo 'MRBK[[:digit:]]+' <<< $line)
mv "$line" "$dir/$newfil"
done

sh/bash: Find all files in a folder that start with a number followed by a _blank_

I am working on a shell script, and now I came to a point where I need to rename files that start with a number and a blank by removing this pattern and moving them to a specific folder that is basically the string between the second and third " - "
example :
001 - folder1 - example.doc > /folder1/example.doc
002 - folder2 - someexample.doc > /folder2/someexample.doc
003 - folder3 - someotherexample.doc > /folder3/someotherexample.doc
I want to do something like
find /tmp -name '*.doc' -print | rename .... ...
what I do not know is:
- how to tell find that the file starts with a number,
and second
- how to explode the name by a pattern like " - " and tell rename to place the file in the folder
If possible, I would avoid find and just use bash's regular expression matching. If you don't need to recursively search /tmp (as your version of find is doing), this should work in any version of bash:
regex='^[[:digit:]]+ - (.+) - (.+)$'
for f in /tmp/*.doc; do
[[ $f =~ /tmp/$regex ]] || continue
mv -- "$f" "/${BASH_REMATCH[1]/${BASH_REMATCH[2]}"
done
If you do need to recursively search, and you are using bash 4 or later, you can use the globstar option:
shopt -s globstar
regex='^[[:digit:]]+ - (.+) - (.+)$'
for f in /tmp/**/*.doc; do
b=$(basename "$f")
[[ $b =~ $regex ]] || continue
mv -- "$f" "/${BASH_REMATCH[1]/${BASH_REMATCH[2]}"
done
If your destination folder and file names have no spaces, and if all your original files are in the current directory, you could try something like:
while read f; do
if [[ "$f" =~ ^\./[0-9]+\ -\ ([^\ ]+)\ -\ (.+\.doc)$ ]]; then
mkdir -p "${BASH_REMATCH[1]}"
mv "$f" "${BASH_REMATCH[1]}/${BASH_REMATCH[2]}"
fi
done < <(find . -maxdepth 1 -name '[0-9]*.doc')
Explanations:
find . -maxdepth 1... restricts the search to the current directory.
find...-name '[0-9]*.doc matches only files which names start with at least one digit and end with .doc.
The regular expression models your original file names (with the initial ./ that find adds). It contains two sub-expressions enclosed in (...) corresponding to the folder name and to the destination file name. These sub-expressions are stored by bash in the BASH_REMATCH array if there is a match, at positions 1 and 2, respectively.
The regular expression removes leading and trailing spaces from the destination folder name and the leading spaces from the destination file name (I assume this is what you want).
With gawk:
find . -regex '^.*[0-9]+ - [a-zA-Z]+[0-9] - [a-zA-Z]+\.doc$' -printf %f | awk -F- '{ print "mv \""$0"\" /"gensub(" ","","g",$2)"/"gensub(" ","","g",$3) }'
Use find with regular expressions and then parse the output through to awk to execute the move command.
When you have verified that the commands are OK, run the commands with.
find . -regex '^.*[0-9]+ - [a-zA-Z]+[0-9] - [a-zA-Z]+\.doc$' -printf %f | awk -F- '{ print "mv \""$0"\" /"gensub(" ","","g",$2)"/"gensub(" ","","g",$3) }' | sh
Be wary that such a strategy can be open to a risk of command injection.
the answer is very close to the suggestion made by Raman, I removed printf, added the part that creates the folder and added the part removing the leading and the trailing blank.
in the end it looks like this:
find . -regex '^.*[0-9].*\.doc$' | awk -F- '{ print "mkdir \""gensub(" $","","g",gensub("^ ","","g",$2))"\" \nmv \""$f"\" \"./"gensub(" $","","g",gensub("^ ","","g",$2))"/"gensub(" $","","g",gensub("^ ","","g",$2"-"$3))"\"" }'|sh
thank you everybody for the suggestions.

Find all files in dir/subdirs and save into an array but also rename files with spaces to underscore

I'm able to find all files in a directory and its subdirectories. I save it into an array but I also need to rename ones that have spaces in their names to an underscore.
Sample structure
./abc 123.txt
./dags/ftp.pyc
./dags/ftp.py
./logs/scheduler/2017-05-12/ftp.py.log
Find the files and insert into array
array=($(find . -type f -print0 | xargs -0))
# Does not work
for i in ${array[#]};do echo ${i// /_};done
#Output
./abc
123.txt
./dags/ftp.pyc
./dags/ftp.py
./logs/scheduler/2017-05-12/ftp.py.log
It would be more ideal if I could run regex against the value before it goes into the array.
The problem with the above command is that the array is looping through the filename with space as two variables and not one.
Something like below should work
find . -type f -print0 | while IFS= read -r -d '' file; do echo ${file// /_} ; done

Script for renaming files - removing dash chars from Android resource files

The following scripts finds all files in the current directory (recursively) and replaces dash - characters to underscore chars in file names
find . -type f -name '*.png' | while read FILE ; do
newfile="$(echo ${FILE} |sed -e 's/-/_/g')";
mv "${FILE}" "${newfile}" ;
done
given a file in the path that contains dashes, for example, drawable-hdpi/file-name.png, the script will try to rename it to drawable_hdpi/file_name.png - replacing the dash in the directory name as well as in the file name.
I would like to avoid modifying the directory path, and only rewrite the file name.
Any suggestions on how to modify the sed usage to skip the directory path?
The shell has some nifty string operators to chop parts of variables. Your shell manual page has all the details. Here's how I would use them:
find . -type f -name '*.png' |
while read FILE; do
dir=${FILE%/*}
newfile=$(echo "${FILE##*/}" |sed -e 's/-/_/g')
mv "${FILE}" "${dir}/${newfile}"
done
Explanation:
dir=${FILE%/*} chops the shortest part from the right that matches the /* glob, removing the slash and file name, giving the directory.
${FILE##*/} removes the longest part from the left matching */, i.e. leaves just the file name.

bash: trouble with find and sed in a directory

Pulling my hair out - somebody save me from an early Q-ball.
I have a folder with loads of powerpoint files and I want to change a substring in each title. All of them are of the form "lecture 2 2014.pptx" and I want to change "2014" to "2016".
Insider the directory I try commands like:
find . -name "*2014*" | xargs -0 sed -i 's/2014/2016/g'
to no avail. Any advice?
Edit my goal is to change the file name. "Lecture 2 2014.pptx" to "Lecture 2 2016.pptx"
rename s/2014/2016/ *2014.pptx
If your list is too long to expand by shell try:
find -name \*2014.pptx -exec rename s/2014/2016/ {} \;
rename was already mentioned. Be aware that there are two version floating around: one with the syntax
rename [options] expression replacement file...
and one with the syntax
rename s/old/new/ file...
As an alternative: a simple Bash loop with a regex extracting the "2014" from each file name, replacing it with "2016"
re='(.*)2014(.*)'
for fname in *2014*; do
[[ $fname =~ $re ]]
mv "$fname" "${BASH_REMATCH[1]}2016${BASH_REMATCH[2]}"
done

Resources