Nested for loops for directory in bash script - bash

for d in dirlist;
do
cd $d
echo $PWD "primul"
for p in *.HEIC;
do
echo $PWD "din al doilea"
if [ -e "COPY ${p%.*}.jpg" ]; then
echo "COPY ${p%.*}.jpg exists"
else
convert $p "COPY ${p%.*}.jpg"
echo "COPY ${p%.*}.jpg created"
fi
done
done
I have written a script to convert all files that are .HEIC to .jpg. Now I have a structure of folders that is
/main/2019/01
/main/2019/03
/main/2018/12
/main/2018/11
/main/2018/10
/main/2018/09
And I want to run the second for in each directory.
What I get when I run the commands below is only a list of directories and a mention that there are no files to convert.
How do I change the directory using the first for and run the second for in that directory?

cd - changes to the previous directory. That one solution to put it after the first done (second for cycle).
pushd and popd using stack to handle directory changes
check out: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html

Related

Selectively create folders based on names in bash script

I have several files called as follow:
dosulepin3D_CID_5284550.pdbqt
protriptyline3D_CID_4976.pdbqt
These are small molecules. Now I want to create a file in the Results folder for each one of these molecules, ignoring the 3D_CID_5284550.pdbqt they all have behind, and have folders called:
dosulepin
protriptyline
I want to do this with a for loop, since I'm also performing some functions with these files. This is what I have:
DIR="/home/roy/MolecularDocking/VirtualScreening/Dockings"
cd $DIR
for ligand in ligands/*; do
echo $ligand
mkdir "/home/roy/MolecularDocking/Results/$ligand"
done;
But this obviuosly creates folders with the full name.
Something like this.
DIR="/home/roy/MolecularDocking/VirtualScreening/Dockings"
cd "$DIR" || exit
for ligand in ligands/*.pdbqt; do
echo "$ligand"
echo mkdir -p "/home/roy/MolecularDocking/Results/${ligand%3D*}"
done
Remove the echo from the mkdir if you're satisfied with the output.
See Parameter Expansion for more details.

BASH Script for creating multiple directories, moving files, and then renaming said files

I am trying to make a bash script to create directories with the same name as each file in a given directory, then move said files to their respective directories, and then rename the files.
Basically - a quantum chemistry program that I use requires that the input files be named "ZMAT". So, if I have multiple jobs, I currently need to manually create directories, and then move the ZMAT files into them (can only run one job per folder).
When I run my code, I get "binary operator expected". I am not sure what this means. Some help please.
Here is what I have so far:
#!/bin/bash
if [ -e *.ZMAT ];
then
echo "CFOUR Job Detected"
for INPFILE in *.ZMAT; do
BASENAME=$(basename $INPFILE )
INPFILE=$BASENAME.ZMAT
OUTFILE=$BASENAME.out
XYZFILE=$BASENAME.xyz
ERRORFILE=$BASENAME.slu
if [ ! -e $ERRORFILE ];
then
# Create folder in scratch directory with the basename
mkdir /scratch/CFOUR/$BASENAME
# Move the file to its directory
mv -f $INPFILE /scratch/CFOUR/$BASENAME
# cd to the new directory
cd /scratch/CFOUR/$BASENAME
# Change the file name to just ZMAT
mv -f $INPFILE ZMAT
echo "Submitting CFOUR Job"
# Submit to scheduler
#RUN_COMMAND="sbatch -J $BASENAME _CFOUR_MRCC_SLURM.SUB"
#eval $RUN_COMMAND
else
echo "Error File Detected - Not Submitting Job"
fi
done
fi
An alternative would be to create symlinks to the original files.
As you said before, each ZMAT symlink would need to be in its own directory.
The upside is that the original data doesn't move, so less risk of breaking it, but the tool you want to use should read the symlinks as if they are the files it is looking for.
This one-liner creates an out directory in the current folder that you could subsequently move wherever you want it. You could easily create it where you do want it by replacing "out" with whatever absolute path you wanted
for i in *.ZMAT; do mkdir -p out/$i ; ln -s $PWD/$i out/$i/ZMAT ; done
I believe I have solved my problem. Here is the new script, which appears to be working fine. Any input is welcome though!
#!/bin/bash
SUBDIR=$(pwd)
for i in *.ZMAT; do
BASENAME=$(basename $i .ZMAT)
INPFILE=$BASENAME.ZMAT
OUTFILE=$BASENAME.out
XYZFILE=$BASENAME.xyz
ERRORFILE=$BASENAME.slu
if [ ! -e $ERRORFILE ];
then
mkdir /scratch/CFOUR/$BASENAME # Create Scratch Folder
cp $INPFILE /scratch/cdc/CFOUR/$BASENAME # Move Input to Scratch
cd /scratch/CFOUR/$BASENAME #cd to Scratch Folder
mv -f $INPFILE ZMAT # Change Input Name
echo "Submitting CFOUR Job"
# Submit to scheduler
#RUN_COMMAND="sbatch -J $BASENAME _CFOUR_MRCC_SLURM.SUB"
#eval $RUN_COMMAND
cd $SUBDIR #Go back to SUBDIR
else
echo "Error File Already Exists"
fi
done

Linux shell script to copy and rename multiple files

I have this snippet:
#!/bin/bash
parent=/parent
newfolder=/newfolder
mkdir "$newfolder"
for folder in "$parent"/*; do
if [[ -d $folder ]]; then
foldername="${folder##*/}"
for file in "$parent"/"$foldername"/*; do
filename="${file##*/}"
newfilename="$foldername"_"$filename"
cp "$file" "$newfolder"/"$newfilename"
done
fi
done
I do need to turn it around in a way that the copied files would be named after the folder they are being moved to (e.g. moving to the /root/Case22 files would be renamed to case22_1.jpg, case22_2.docx, case22_3.JPG etc). The files would be copied from USB and both destination and source directries would be entered by the user. I have written everything else and it works apart from actual renaming and thought I could adapt this snippet.
thanks
p.s. the snippet is written by Jahid and found on stackoverflow
you can try something like this;
#!/bin/bash
parent=/root
a=1
for file in $parent/Case22*; do
filename="${file%.*}"
extension="${file##*.}"
newfilename=$(printf "$filename"_"$a"."$extension")
mv -- "$file" "$newfilename"
let a=a+1
done
Thanks for the help. I have found the solution and thought I might post it here in case someone else will be looking at this.
As the title suggests I needed a Linux shell script to copy and rename multiple files keeping original directory tree (the file source and archive locations would be specified by the user of the script). Here is the code that I came up with after few days research of different sources (it includes a trap so only one instance of script would be running at a time):
lockdir=/var/tmp/mylock #directory for lock file creation
pidfile=/var/tmp/mylock/pid #directory to get the process ID number
if ( mkdir ${lockdir} ) 2> /dev/null; then #main argument to create lock file
echo $$ > $pidfile #if successful script will proceed, otherwise it will skip to the else part of the statement at the end of the script
trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT #trap to capture reasons of script termination and removal of the lock file so it could be launched again
#start of the main script body, part of successful "if" statement
# read entry_for_testing_only #request for user entry to keep script running and try to run another script instance
findir="$2/$(basename "$1")" #variable that defines final directory to which files from USB will be copied
if [ ! -d "$1" ]; then #testing if first directory entry is a valid directory’’
echo "$1" "is not a directory"
echo ""
exit
else
if [ ! -d "$2" ]; then #testing if second entry is a valid directory
echo "archive directory non existant"
exit
else
if [ -d "$findir" ] && [ "$(ls -A "$findir")" ]; then #testing if second entry directory contains the same name folders and if the folders are empty - to avoid file overwriting
echo "such folder already there and it's not empty"
exit
else
if [ ! -d "$findir" ] ; then #last archive directory argument to create final archive directory
mkdir "$findir"
else true
fi
fi
fi
fi
rsync -a "$1"/ "$findir" #command to copy all files from the source to the archive retaining the directory tree
moved_files="$(find "$findir" -type f)" #variable that finds all files that have been copied to the archive directory
for file in $moved_files; do #start of the loop that renames copied files
counter="$((counter+1))" #incrementation variable
source_name="$(basename "$1")" #variable that captures the name of the source directory
new_name="$source_name"_"$counter" #variable that puts start of the file name and incrementation element together
if echo "$file" | grep "\." #argument that captures the extension of the file
then
extension="$(echo "$file" | cut -f2 -d. )"
else
extension=
fi
full_name="$new_name"."$extension" #variable that defines the final new name of the file
dir="$(dirname "${file}")" #variable that captures the directorry address of currently looped file
mv "$file" "$dir/$full_name" #move command to rename currently looped file with the final new name
done
#end of the main script body, unsuccessful "if" statement continues here
else
echo "Another instance of this script is already running. PID: $(cat $pidfile)"
fi

Script to rename, move and compress images automagically

I'm trying to create a script on my Raspberry's Xbian that will "watch" a folder and compress any folders with images I saved there from Google Image Search.
What I want: The script will move all folders with " - Google Search" in their name to a temp folder, rename them removing the " - Google Search" part, leaving only the subject of the search query. Then, it will sequentially number the files in each folder using the folder name / the search query as their new name. So, "Random file.jpg" and "anoth3r_rand0m_file.png" will become "search_topic_01.jpg" and "search_topic_02.jpg".
Then, they'll be all moved to another folder, an "Incoming Images" one, where ImageMagick will do its magic on them, at the same time moving them to a "Ready Images" folder.
Still with me?
Here's what I got so far, from bundling stuff I found online together with my limited knowledge of Bash scripting:
echo "making temp"
mkdir /media/dBox/downloads/Xyma/temp
wait
echo "moving files"
mv /media/dBox/downloads/Xyma/*Google\ Search /media/dBox/downloads/Xyma/temp
wait
echo "renaming folders"
rename s/\ -\ Google\ Search// /media/dBox/downloads/Xyma/temp/*
wait
echo "renaming files"
for dir in /media/dBox/downloads/Xyma/temp/; do
if test -d "$dir"; then
(
cd $dir
for file in *; do
newfile=$dir.${file#*.}
mv "$file" "$newfile"
done
)
fi
done
wait
echo "making ready subfolder"
mkdir /media/dBox/downloads/Xyma/temp/00_Unreg_Ready_Image_Folders
wait
echo "moving folders to ready folder"
mv /media/dBox/downloads/Xyma/temp/* /media/dBox/downloads/Xyma/00_Unreg_Ready_$
wait
echo "removing temp folder"
rmdir /media/dBox/downloads/Xyma/temp
...and let's just say "AAARGHRGHRGHHhh".
I'm sure that there must be an even simpler way, with, say, a five-word command and maybe two parameters, that will auto magically do everything and sprinkle it with stardust, or generally "a simpler and better way to do it", but it's currently slipping my mind.
So, I'm open to ideas and suggestions.
Any help? Anyone?
Help!

OSX Bash Script working with Files/Folders with spaces in the name

I'm trying to build a BASH script on OS X (10.6/10.7) to process a folder called QCExports which has folders with people's names in it in the format "LAST, First", i.e. "BOND, James".
When I run the following script, everything works but it falls over on folder or filenames with a space in them.
Script Code:
#!/bin/bash
echo "QuickCeph Export Script"
#Set Path to Process & Paths to Copy To
pathToQCExports=/Users/myuser/Desktop/QCExports
sureSmilePath=/Users/myuser/Desktop/QCExportsForSureSmile
sesamePath=/Users/myuser/Desktop/QCExportsForSesame
blankReplace=""
#Process Each Name
find $pathToQCExports -type d | while read name ; do
#escaping the folder with a space in the name
nameParsed=${name/", "/",\ "}
echo "Processing: "$nameParsed
pathSureSmile=${nameParsed/$pathToQCExports/$sureSmilePath}
pathSesame=${nameParsed/$pathToQCExports/$sesamePath}
mkdir $pathSesame
mkdir $pathSureSmile
echo "Folders Created"
#Copy For SureSmile
echo ${pathSureSmile}"/Suresmile-Frontal\ Photo.jpg" ${pathSureSmile}"/Suresmile-Frontal\ Photo.jpg"
#cp `${$pathSureSmile}"/Suresmile-Frontal\ Photo.jpg" ${pathSureSmile}"/Suresmile-Frontal\ Photo.jpg"`
#Copy For Sesame
echo ${pathSesame}"/Suresmile-Frontal\ Photo.jpg" ${pathSesame}"/S02.jpg"
#cp `${pathSesame}"/Suresmile-Frontal\ Photo.jpg" ${pathSesame}"/S02.jpg"`
done
echo "Completed";
Output:
QuickCeph Export Script
Processing: /Users/myuser/Desktop/QCExports/BOND,\ James
mkdir /Users/myuser/Desktop/QCExportsForSesame/BOND,\ James
mkdir: James: File exists
mkdir /Users/myuser/Desktop/QCExportsForSureSmile/BOND,\ James
mkdir: James: File exists
Folders Created
/Users/myuser/Desktop/QCExportsForSureSmile/BOND,\ James/Suresmile-Frontal\ Photo.jpg /Users/myuser/Desktop/QCExportsForSureSmile/BOND,\ James/Suresmile-Frontal\ Photo.jpg
/Users/myuser/Desktop/QCExportsForSesame/BOND,\ James/Suresmile-Frontal\ Photo.jpg /Users/myuser/Desktop/QCExportsForSesame/BOND,\ James/S02.jpg
Completed
On OS X usually in the terminal, you use a \ to escape a space in a folder or filename, but that doesn't seem to work.
I notice that it's interpreting the spaces as a normal space would be interpreted on the command line and thinking I want to execute the command on two files - i.e. it's not passing the \ onwards. I end up with a folder called "Bond,\" and a folder called "James" in the folder I execute the script from.
Note, I deliberately have the cp commands # out at the moment, so they aren't being executed... the problem is the same for both creating the folders & copying the filenames.
If I copy and paste the "echo'd" version of these commands into a terminal window, the commands work! But when BASH executes them, it doesn't respect the . :(
Any ideas?
Thanks!!
John
See my modifications on your script, you don't have to substitute spaces like you tried.
Moreover, you must choose if you backslash the spaces or if you are using quotes.
The simple way is to use double quotes.
Good doc about quotes, see http://mywiki.wooledge.org/Quotes and http://wiki.bash-hackers.org/syntax/words
#!/bin/bash
echo "QuickCeph Export Script"
#Set Path to Process & Paths to Copy To
pathToQCExports=/Users/myuser/Desktop/QCExports
sureSmilePath=/Users/myuser/Desktop/QCExportsForSureSmile
sesamePath=/Users/myuser/Desktop/QCExportsForSesame
blankReplace=""
#Process Each Name
find $pathToQCExports -type d | while read nameParsed ; do
echo "Processing: $nameParsed"
pathSureSmile="${nameParsed/$pathToQCExports/$sureSmilePath}"
pathSesame="${nameParsed/$pathToQCExports/$sesamePath}"
mkdir "$pathSesame"
mkdir "$pathSureSmile"
echo "Folders Created"
#Copy For SureSmile
echo "${pathSureSmile}/Suresmile-Frontal Photo.jpg" "${pathSureSmile}/Suresmile-Frontal Photo.jpg"
#Copy For Sesame
echo "${pathSesame}/Suresmile-Frontal Photo.jpg" "${pathSesame}/S02.jpg"
done
echo "Completed"

Resources