Linux shell script to copy and rename multiple files - shell

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

Related

Check if file exist relative to current script (one level up)

How to check/get file path relative to current script?
Script is running from ..../app/scripts/dev.sh
File to check from ..../app/dist/file.js
dir="${BASH_SOURCE%/*}../dist/backend.js"
if [ -f ${dir} ]; then
echo "file exists. ${dir}"
else
echo "file does not exist. ${dir}"
fi
There are three problems in your script.
To store the output of a command in a variable, use $(), not ${}.
[ -f "$dir" ] checks if $dir is a a file, which will never happen, because dirname outputs a directory.
Your script can be executed from any other working directory as well. Just because the script is stored in ···/app/scripts/ does not mean it will always run from there.
Try
file=$(dirname "$BASH_SOURCE")/../dist/file.js
if [ -f "$file" ]; then
echo "file exists."
else
echo "file does not exist."
fi

How to get a list of subdirectories from a file and then create those subdirectories in a directory?

When a user inputs a name, there should be a new directory that gets created under that name.
In addition to that, the script needs to consult a file structure1.txt which is found in /etc/scriptbuilder/str1.
In this file, it will list two subdirectories (one on each line), the script is then supposed to create these two subdirectories in the new directory the user just made and named.
So how can the script then create each of the subdirectories that are listed in this text file?
I'm completely lost on that part.
This is my code so far:
echo "Enter the project name "
read name
echo $name
if [ ! -d $name ] then
mkdir $name
else
echo "The project name you entered already exists"
fi
cp /etc/scriptbuilder/str1/structure1.txt /$name
#I know this is wrong
because this would just copy the file over to the new directory but not actually
make the two subdirectories that are on the file onto the new directory
The bash command that you are looking for is read.
Also the syntax for your if [ ! -d "$name" ] should have a semicolon.
The else would typically have an exit 1 (or some such value).
Typical bash code gets input from the command line, but what you want is fine.
For testing purposes, I inserted a ~ (tilde), which references your home directory.
The script should look something like:
filename="/etc/scriptbuilder/str1"
read -p "Enter the project name " name
echo "$name"
if [ ! -d ~/"$name" ]; then
mkdir ~/"$name"
else
echo "The project name you entered already exists"
exit 1
fi
while read -r line; do
mkdir ~/"$name/$line"
done < "$filename"
You can clean up the formatting.

Zip I/O error: No such file or directory in bash script

I've been writing some code to (mostly) automate password protecting and compressing/archiving folders and files. However, the code seems to hate me.
I've run it line by line, and it always chokes on the zip command, even when the $file and $source are correct and valid.
Any ideas?
Here's the source code:
#!/bin/bash
echo "Drag in the source file"
read source
echo
echo "Drag in the destination file, or press enter to select the Desktop"
read destination
echo
if [ -z "$destination" ]
then destination="$PWD/Desktop"
echo "Destination is set to the desktop"
fi
echo "Type the name of the file to make"
read file
if [ -z "$file" ]
then file="archive"
echo "File name set to archive.zip"
fi
file="${destination}/${file}"
if [ -d $"source" ]
then zip -erj "$file" "$destination"
else zip -ej "$file" "$destination"
fi
There are a couple of problems in your code:
if [ -z "$destination" ]
then destination="$PWD/Desktop"
echo "Destination is set to the desktop"
fi
$PWD is the current working directory. It is your home directory when you open a Terminal but it changes everytime you run cd.
The Desktop directory is $HOME/Desktop.
If you don't run the script from your home directory, most probably $PWD/Desktop doesn't exist and this is a cause for errors; zip doesn't attempt to create the destination directory for the archive you ask it to build. If the directory doesn't already exist it displays an error message and exits.
Another problem is on the invocation of zip:
if [ -d $"source" ]
then zip -erj "$file" "$destination"
else zip -ej "$file" "$destination"
fi
You probably want to archive the file $source or the files in the $source directory (if it is a directory) but you mistakenly put $destination as the file/directory to archive in the zip command line.
if [ -d $"source" ] -- it should be "$source", otherwise the quote are useless and if $source contains spaces the script will exit with a syntax error.
One last thing: zip doesn't mind receiving -r in the command line when it is asked to archive only one file. You can replace the entire if/else block above with a single command:
zip -erj "$file" "$source"

Bash script with a loop not executing utility that has paramters passed in?

Anyone able to help me out? I have a shell script I am working on but for the loop below the command after "echo "first file is $firstbd" is not being executed.. the $PROBIN/proutil ?? Not sure why this is...
Basically I have a list of files in a directory (*.list), I grab them and read the first line and pass it as a parameter to the cmdlet then move the .list and the content of the .list to another directory (the .list has a list of files with full path).
for i in $(ls $STAGEDIR/*.list); do
echo "Working with $i"
# grab first .bd file
firstbd=`head -1 $i`
echo "First file is $firstbd"
$PROBIN/proutil $DBENV/$DBNAME -C load $firstbd tenant $TENANT -dumplist $STAGEDIR/$i.list >> $WRKDIR/$i.load.log
#move the list and its content to finished folder
binlist=`cat $i`
for movethis in $binlist; do
echo "Moving file $movethis to $STAGEDIR/finished"
mv $movethis $STAGEDIR/finished/
done
echo "Finished working with list $i"
echo "Moving it to $STAGEDIR/finished"
mv $i $STAGEDIR/finished/
done
The error I was getting is..
./tableload.sh: line 107: /usr4/dlc/bin/proutil /usr4/testdbs/xxxx2 -C load /usr4/dumpdir/xxxxx.bd tenant xxxxx -dumplist /usr4/dumpdir/PUB.xxxxx.list >> /usr4/dumpdir/PUB.xxxx.list.load.log: A file or directory in the path name does not exist... however if I run "/usr4/dlc/bin/proutil"
The fix was to remove ">> $WRKDIR/$i.load.log".. the binary utility wouldn't run when trying to output results to file.. strange..
A couple of really bad practices here
parse the output of ls
not quoting variables
iterating the lines of a file with cat and for
As shelter comments, you don't check that you've created all the directories in the path for your log file.
A rewrite:
for i in "$STAGEDIR"/*.list; do
echo "Working with $i"
# grab first .bd file
firstbd=$(head -1 "$i")
echo "First file is $firstbd"
# ensure the output directory exists
logfile="$WRKDIR/$i.load.log"
mkdir -p "$(dirname "$logfile")"
"$PROBIN"/proutil "$DBENV/$DBNAME" -C load "$firstbd" tenant "$TENANT" -dumplist "$STAGEDIR/$i.list" >> "$logfile"
# move the list and its content to finished folder
while IFS= read -r movethis; do
echo "Moving file $movethis to $STAGEDIR/finished"
mv "$movethis" "$STAGEDIR"/finished/
done < "$i"
echo "Finished working with list $i"
echo "Moving it to $STAGEDIR/finished"
mv "$i" "$STAGEDIR"/finished/
done

Shell Script to copy all files in a directory to a specified folder

I'm new in shell script and I am trying to figure out a way to write a script that copies all the files in the current directory to a directory specified from a .txt file and if there are matching names, it adds the current date in the form of FileName_YYYYMMDDmmss to the name of the file being copied to prevent overwritting.
Can someone help me out?
I saw thinking something along the lines of
#!/bin/bash
source=$pwd #I dont know wheter this actually makes sense I just want to
#say that my source directory is the one that I am in right now
destination=$1 #As I said I want to read the destination off of the .txt file
for i in $source #I just pseudo coded this part because I didn't figure it out.
do
if(file name exists)
then
copy by changing name
else
copy
fi
done
the problem is I have no idea how to check whether the name exist and copy and rename at the same time.
Thanks
How about this? I am supposing that the target directory is in the
file new_dir.txt.
#!/bin/bash
new_dir=$(cat new_dir.txt)
now=$(date +"%Y%m%d%M%S")
if [ ! -d $new_dir ]; then
echo "$new_dir doesn't exist" >&2
exit 1
fi
ls | while read ls_entry
do
if [ ! -f $ls_entry ]; then
continue
fi
if [ -f $new_dir/$ls_entry ]; then
cp $ls_entry $new_dir/$ls_entry\_$now
else
cp $ls_entry $new_dir/$ls_entry
fi
done
I guess this what you are looking for :
#!/bin/bash
dir=$(cat a.txt)
for i in $(ls -l|grep -v "^[dt]"|awk '{print $9}')
do
cp $i $dir/$i"_"$(date +%Y%m%d%H%M%S)
done
I assumed that a.txt contains only the name of the destination directory. If there are other entries, you should add some filter to the first statement(using grep or awk).
NB: I used full time stamp(YYYYMMDDHHmmss) instead of your YYYYMMDDmmss as it doesn't seem logical.

Resources