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

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.

Related

Copy specific files from different directories without changing file extension

I have a bunch of image files named as 1.jpg in several directories I want to copy that into another.
I tried using the below command (consider 100 folders)
find folder/{1..100} -type f -name '1.*' -exec cp --backup=numbered "{}" folder/new/ \;
but the file extension get changed as below
1.jpg.~1~
1.jpg.~2~
1.jpg.~3~
...
I am expecting my output should look something like below
1.~1~.jpg
1.~2~.jpg
1.~3~.jpg
...
(or)
1(1).jpg
1(2).jpg
1(3).jpg
...
Note: I am using {1..100} so that folder order starts from 1, 2, 3, 4...100 and not as 1, 10, 11,...100, 2, 20, 21, 22...
Is there any way I could find and copy images without changing the file extension?
Thank you in advance!
Assuming you want to use the subfolder name (number) as a suffix to the new
file name, would you please try:
#!/bin/bash
folder="folder" # directory name
base="1" # basename of the jpg file
ext="jpg" # extention of the jpg file
new="new" # directory name for the "new" files
mkdir -p "$folder/$new" # create "new" directory if nonexistent
while IFS= read -r -d "" f; do
n=${f#*$folder/}; n=${n%/$base.$ext}
echo cp -i -- "$f" "$folder/$new/$base($n).$ext"
done < <(find "$folder" -type f -regex "$folder/[0-9]+/$base\.$ext" -print0)
find "$folder" -type f -regex "$folder/[0-9]+/$base\.$ext" finds
all numbered folders which contains "1.jpg". You do not have to specify
the range between 1 and 100.
-print0 option uses a null character to delimit filenames. It is useful
to protect filenames which may contain special characters such as blank
characters.
The output of find command, matched filenames delimited by null characters,
is redirected to the read command within the while loop.
The -d "" option to the read command splits the input on null characters
corresponding to -print0.
n=${f#*$folder/}; n=${n%/$base.$ext} assigns n to the subfolder name
which contains the jpg file.
"$folder/$new/$base($n).$ext" constructs the new filename rearranging
the substrings.
If the output of the echo command looks okay, drop echo.
Do you accept a solution in two times?
Run your find command
Run this command:
for f in folder/new/*.~*~; do
idx="${f##*.}";
new=${f%.${idx}};
idx="${idx//\~/}";
ext="${new##*.}";
new="${new%.${ext}}";
new="${new}(${idx}).${ext}";
echo mv "$f" "$new";
done
Only based on bash remove %, #, %% and ## matching patterns.
get index pattern from original filename (f)
new filename (new) is original filename without index extension (~*~)
remove ~ characters in index token (idx)
get original filename extension (ext)
remove original filename extension in new filename
create new filename (with format as you want)
rename original filename to new filename
Notes:
Remove new line characters if you want (just here for presentation)
After test, remove echo before mv command.

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

using find with variables in bash

I am new to bash scripting and need help:
I need to remove specific files from a directory . My goal is to find in each subdirectory a file called "filename.A" and remove all files that starts with "filename" with extension B,
that is: "filename01.B" , "filename02.B" etc..
I tried:
B_folders="$(find /someparentdirectory -type d -name "*.B" | sed 's# (.*\)/.*#\1#'|uniq)"
A_folders="$(find "$B_folders" -type f -name "*.A")"
for FILE in "$A_folders" ; do
A="${file%.A}"
find "$FILE" -name "$A*.B" -exec rm -f {}\;
done
Started to get problems when the directories name contained spaces.
Any suggestions for the right way to do it?
EDIT:
My goal is to find in each subdirectory (may have spaces in its name), files in the form: "filename.A"
if such files exists:
check if "filename*.B" exists And remove it,
That is: remove: "filename01.B" , "filename02.B" etc..
In bash 4, it's simply
shopt -s globstar nullglob
for f in some_parent_directory/**/filename.A; do
rm -f "${f%.A}"*.B
done
If the space is the only issue you can modify the find inside the for as follows:
find "$FILE" -name "$A*.B" -print0 | xargs -0 rm
man find shows:
-print0
True; print the full file name on the standard output, followed by a null character (instead of the newline character that -print uses). This allows
file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corre-
sponds to the -0 option of xargs.
and xarg's manual
-0 Input items are terminated by a null character instead of by whitespace, and the quotes and backslash are not special (every character is taken literal-
ly). Disables the end of file string, which is treated like any other argument. Useful when input items might contain white space, quote marks, or
backslashes. The GNU find -print0 option produces input suitable for this mode.

Recursively concatenating (joining) and renaming text files in a directory tree

I am using a Mac OS X Lion.
I have a folder: LITERATURE with the following structure:
LITERATURE > Y > YATES, DORNFORD > THE BROTHER OF DAPHNE:
Chapters 01-05.txt
Chapters 06-10.txt
Chapters 11-end.txt
I want to recursively concatenate the chapters that are split into multiple files (not all are). Then, I want to write the concatenated file to its parent's parent directory. The name of the concatenated file should be the same as the name of its parent directory.
For example, after running the script (in the folder structure shown above) I should get the following.
LITERATURE > Y > YATES, DORNFORD:
THE BROTHER OF DAPHNE.txt
THE BROTHER OF DAPHNE:
Chapters 01-05.txt
Chapters 06-10.txt
Chapters 11-end.txt
In this example, the parent directory is THE BROTHER OF DAPHNE and the parent's parent directory is YATES, DORNFORD.
[Updated March 6th—Rephrased the question/answer so that the question/answer is easy to find and understand.]
It's not clear what you mean by "recursively" but this should be enough to get you started.
#!/bin/bash
titlecase () { # adapted from http://stackoverflow.com/a/6969886/874188
local arr
arr=("${#,,}")
echo "${arr[#]^}"
}
for book in LITERATURE/?/*/*; do
title=$(titlecase ${book##*/})
for file in "$book"/*; do
cat "$file"
echo
done >"$book/$title"
echo '# not doing this:' rm "$book"/*.txt
done
This loops over LITERATURE/initial/author/BOOK TITLE and creates a file Book Title (where should a space be added?) from the catenated files in each book directory. (I would generate it in the parent directory and then remove the book directory completely, assuming it contains nothing of value any longer.) There is no recursion, just a loop over this directory structure.
Removing the chapter files is a bit risky so I'm not doing it here. You could remove the echo prefix from the line after the first done to enable it.
If you have book names which contain an asterisk or some other shell metacharacter this will be rather more complex -- the title assignment assumes you can use the book title unquoted.
Only the parameter expansion with case conversion is beyond the very basics of Bash. The array operations could perhaps also be a bit scary if you are a complete beginner. Proper understanding of quoting is also often a challenge for newcomers.
cat Chapters*.txt > FinaleFile.txt.raw
Chapters="$( ls -1 Chapters*.txt | sed -n 'H;${x;s/\
//g;s/ *Chapters //g;s/\.txt/ /g;s/ *$//p;}' )"
mv FinaleFile.txt.raw "FinaleFile ${Chapters}.txt"
cat all txt at once (assuming name sorted list)
take chapter number/ref from the ls of the folder and with a sed to adapt the format
rename the concatenate file including chapters
Shell doesn't like white space in names. However, over the years, Unix has come up with some tricks that'll help:
$ find . -name "Chapters*.txt" -type f -print0 | xargs -0 cat >> final_file.txt
Might do what you want.
The find recursively finds all of the directory entries in a file tree that matches the query (In this case, the type must be a file, and the name matches the pattern Chapter*.txt).
Normally, find separates out the directory entry names with NL, but the -print0 says to separate out the entries names with the NUL character. The NL is a valid character in a file name, but NUL isn't.
The xargs command takes the output of the find and processes it. xargs gathers all the names and passes them in bulk to the command you give it -- in this case the cat command.
Normally, xargs separates out files by white space which means Chapters would be one file and 01-05.txt would be another. However, the -0 tells xargs, to use NUL as a file separator -- which is what -print0 does.
Thanks for all your input. They got me thinking, and I managed to concatenate the files using the following steps:
This script replaces spaces in filenames with underscores.
#!/bin/bash
# We are going to iterate through the directory tree, up to a maximum depth of 20.
for i in `seq 1 20`
do
# In UNIX based systems, files and directories are the same (Everything is a File!).
# The 'find' command lists all files which contain spaces in its name. The | (pipe) …
# … forwards the list to a 'while' loop that iterates through each file in the list.
find . -name '* *' -maxdepth $i | while read file
do
# Here, we use 'sed' to replace spaces in the filename with underscores.
# The 'echo' prints a message to the console before renaming the file using 'mv'.
item=`echo "$file" | sed 's/ /_/g'`
echo "Renaming '$file' to '$item'"
mv "$file" "$item"
done
done
This script concatenates text files that start with Part, Chapter, Section, or Book.
#!/bin/bash
# Here, we go through all the directories (up to a depth of 20).
for D in `find . -maxdepth 20 -type d`
do
# Check if the parent directory contains any files of interest.
if ls $D/Part*.txt &>/dev/null ||
ls $D/Chapter*.txt &>/dev/null ||
ls $D/Section*.txt &>/dev/null ||
ls $D/Book*.txt &>/dev/null
then
# If we get here, then there are split files in the directory; we will concatenate them.
# First, we trim the full directory path ($D) so that we are left with the path to the …
# … files' parent's parent directory—We will write the concatenated file here. (✝)
ppdir="$(dirname "$D")"
# Here, we concatenate the files using 'cat'. The 'awk' command extracts the name of …
# … the parent directory from the full directory path ($D) and gives us the filename.
# Finally, we write the concatenated file to its parent's parent directory. (✝)
cat $D/*.txt > $ppdir/`echo $D|awk -F'/' '$0=$(NF-0)'`.txt
fi
done
Now, we delete all the files that we concatenated so that its parent directory is left empty.
find . -name 'Part*' -delete
find . -name 'Chapter*' -delete
find . -name 'Section*' -delete
find . -name 'Book*' -delete
The following command will delete empty directories. (✝) We wrote the concatenated file to its parent's parent directory so that its parent directory is left empty after deleting all the split files.
find . -type d -empty -delete
[Updated March 6th—Rephrased the question/answer so that the question/answer is easy to find and understand.]

rename fails on filenames with spaces

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.

Resources