I have this function that creates directory with the current date + user input as the name
alias mkdirDate="f1"
function f1() {
export tmp=""
vared -p 'Project name: ' -c tmp
mkdir "$(date +%Y%m%d) $tmp"
fi
}
id like to add the possibility to use options like in rm or find to add functionality.
is there any other way OTHER than something like if [ $tmp='-p' ] touch file.py ?
Related
Good day,
I need your help in creating next script
Every day teacher uploading files in next format:
STUDENT_ACCOUNTS_20200217074343-20200217.xlsx
STUDENT_MARKS_20200217074343-20200217.xlsx
STUNDENT_HOMEWORKS_20200217074343-20200217.xlsx
STUDENT_PHYSICAL_20200217074343-20200217.xlsx
SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx
[file_name+todaydatetime-todaydate.xlsx]
But sometimes a teacher is not uploading these files and we need to do manual renaming the files received for the previous date and then copying every separate file to separate folder like:
cp STUDENT_ACCOUNTS_20200217074343-20200217.xlsx /incoming/A1/STUDENT_ACCOUNTS_20200318074343-20200318.xlsx
cp STUDENT_MARKS_20200217074343-20200217.xlsx /incoming/B1/STUDENT_ACCOUNTS_20200318074343-20200318.xlsx
.............
cp SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/F1/SUBSCRIBED_STUDENTS_20200318074343-20200318.xlsx.
In two words - taking the files from previous date copying them to specific folder with a new timestamp.
#!/bin/bash
cd /home/incoming/
date=$(date '+%Y%m%d')
previousdate="$( date --date=yesterday '+%Y%m%d' )"
cp /home/incoming/SUBSCRIBED_STUDENTS_'$previousdate'.xlsx /incoming/F1/SUBSCRIBED_STUDENTS_'$date'.xlsx
and there could be case when teacher can upload one file and others not, how to do check for existing files?
Thanks for reading that, if you can help me i will ne really thankful - you will save plenty of manual work for me.
The process can be automated completely if your directory structure is known. If it follows some kind of pattern, do mention it here.
For the timing, this maybe helpful:
Filename "tscp"
#
# Stands for timestamped cp
#
tscp() {
local file1=$1 ; shift
local to_dir=$1 ; shift
local force_copy=$1 ; shift
local current_date="$(date '+%Y%m%d')"
if [ "${force_copy}" == "--force" ] ; then
cp "${file1}" "${to_dir}/$(basename ${file1%-*})-${current_date}.xlsx"
else
cp -n "${file1}" "${to_dir}/$( basename ${file1%-*})-${current_date}.xlsx"
fi
}
tscp "$#"
It's usage is as follows:
tscp source to_directory [-—force]
Basically the script takes 2 arguments and the 3rd one is optional.
First arg is source file path and second are is the directory path to where you want to copy (. if same directory).
By default this copy would be made if and only if destination file doesn't exist.
If you want to overwrite the destination file then pass a third arg —force.
Again, this can be refined much much more based on details provided.
Sample usage for now:
bash tscp SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/F1/
will copy SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx to directory /incoming/F1/ with updated date if it doesn't exist yet.
UPDATE:
Give this a go:
#! /usr/bin/env bash
printf_err() {
ERR_COLOR='\033[0;31m'
NORMAL_COLOR='\033[0m'
printf "${ERR_COLOR}$1${NORMAL_COLOR}" ; shift
printf "${ERR_COLOR}%s${NORMAL_COLOR}\n" "$#" >&2
}
alias printf_err='printf_err "Line ${LINENO}: " '
shopt -s expand_aliases
usage() {
printf_err \
"" \
"usage: ${BASH_SOURCE##*/} " \
" -f copy_data_file" \
" -d days_before" \
" -m months_before" \
" -o" \
" -y years_before" \
" -r " \
" -t to_dir" \
>&2
exit 1
}
fullpath() {
local path="$1" ; shift
local abs_path
if [ -z "${path}" ] ; then
printf_err "${BASH_SOURCE}: Line ${LINENO}: param1(path) is empty"
return 1
fi
abs_path="$( cd "$( dirname "${path}" )" ; pwd )/$( basename ${path} )"
printf "${abs_path}"
}
OVERWRITE=0
REVIEW=0
COPYSCRIPT="$( mktemp "/tmp/copyscriptXXXXX" )"
while getopts 'f:d:m:y:t:or' option
do
case "${option}" in
d)
DAYS="${OPTARG}"
;;
f)
INPUT_FILE="${OPTARG}"
;;
m)
MONTHS="${OPTARG}"
;;
t)
TO_DIR="${OPTARG}"
;;
y)
YEARS="${OPTARG}"
;;
o)
OVERWRITE=1
;;
r)
REVIEW=1
COPYSCRIPT="copyscript"
;;
*)
usage
;;
esac
done
INPUT_FILE=${INPUT_FILE:-$1}
TO_DIR=${TO_DIR:-$2}
if [ ! -f "${INPUT_FILE}" ] ; then
printf_err "No such file ${INPUT_FILE}"
usage
fi
DAYS="${DAYS:-1}"
MONTHS="${MONTHS:-0}"
YEARS="${YEARS:-0}"
if date -v -1d > /dev/null 2>&1; then
# BSD date
previous_date="$( date -v -${DAYS}d -v -${MONTHS}m -v -${YEARS}y '+%Y%m%d' )"
else
# GNU date
previous_date="$( date --date="-${DAYS} days -${MONTHS} months -${YEARS} years" '+%Y%m%d' )"
fi
current_date="$( date '+%Y%m%d' )"
tmpfile="$( mktemp "/tmp/dstnamesXXXXX" )"
awk -v to_replace="${previous_date}" -v replaced="${current_date}" '{
gsub(to_replace, replaced, $0)
print
}' ${INPUT_FILE} > "${tmpfile}"
paste ${INPUT_FILE} "${tmpfile}" |
while IFS=$'\t' read -r -a arr
do
src=${arr[0]}
dst=${arr[1]}
opt=${arr[2]}
if [ -n "${opt}" ] ; then
if [ ! -d "${dst}" ] ;
then
printf_err "No such directory ${dst}"
usage
fi
dst="${dst}/$( basename "${opt}" )"
else
if [ ! -d "${TO_DIR}" ] ;
then
printf_err "No such directory ${TO_DIR}"
usage
fi
dst="${TO_DIR}/$( basename "${dst}" )"
fi
src=$( fullpath "${src}" )
dst=$( fullpath "${dst}" )
if [ -n "${OVERWRITE}" ] ; then
echo "cp ${src} ${dst}"
else
echo "cp -n ${src} ${dst}"
fi
done > "${COPYSCRIPT}"
if [ "${REVIEW}" -eq 0 ] ; then
${BASH} "${COPYSCRIPT}"
rm "${COPYSCRIPT}"
fi
rm "${tmpfile}"
Steps:
Store the above script in a file, say `tscp`.
Now you need to create the input file for it.
From you example, a sample input file can be like:
STUDENT_ACCOUNTS_20200217074343-20200217.xlsx /incoming/A1/
STUDENT_MARKS_20200217074343-20200217.xlsx /incoming/B1/
STUNDENT_HOMEWORKS_20200217074343-20200217.xlsx
STUDENT_PHYSICAL_20200217074343-20200217.xlsx
SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/FI/
Where first part is the source file name and after a "tab" (it should be a tab for sure), you mention the destination directory. These paths should be either absolute or relative the the directory where you are executing the script. You may not mention destination directory if all are to be sent to same directory (discussed later).
Let's say you named this file `file`.
Also, you don't really have to type all that. If you have these files in the current directory, just do this:
ls -1 > file
(the above is ls "one", not "l".)
Now we have the `file` from above in which we didn't mention destination directory for all but only for some.
Let's say we want to move all other directories to `/incoming/x` and it exists.
Now script is to be executed like:
bash tscp -f file -t /incoming/x -r
Where `/incoming/x` is the default directory i.e. when none other directory is mentioned in `file`, your files are moved to this directory.
Now in the current directory a script named `copyscript` will be generated which will contain `cp` commands to copy all files. You can open a review `copyscript` and if the copying seems right, go ahead and:
bash copyscript
which will copy all the files and then you can:
rm copyscript
You need not generate to `copyscript` and can straight away go for a copy like:
bash tscp -f file -t /incoming/x
which won't generate any copyscript and copy straight away.
Previously `-r` caused the generation of `copyscript`.
I would recomment to use version with `-r` because that is a little safer and you will be sure that right copies are being made.
By default it would check for the previous day and rename to current date, but you can override that behaviour as:
bash tscp -f file -t /incoming/x -d 3
`-d 3` would look for 3 days back files in `file`.
By default copies won't overwrite i.e. if file at the destination already exists, copies won't be made.
If you want to overwrite, add flag `-o`.
As a conclusion I would advice to use:
bash tscp -f file -r
where file contains tab separated values like above for all.
Also, adding tscp to path would be a good idea after you are sure it works ok.
Also the scipt is made on mac and there is always a change of version clash of tools used. I would suggest to try the script on some sample data first to make sure script works right on your machine.
I'm starting to write bash scripts and I would like to do the following:
a script that deletes the contents of a directory specified in the second argument of the invocation. Like this: example.sh /home/A
Any suggestions?
Thanks!!
Literally find /home/A -delete. Or find "$#" -delete if you want to put it in a script and clean multiple directories.
please use following code
#!/bin/bash
yellow=`tput setaf 3`;
info() {
echo "${yellow}INFO : $# ${reset}";
}
main () {
if [ $# -ne 0 ]; then
S_DIR=$1;
rm -rf "$ACTION";
info "main : delete folder.";
else
info "main : please enter folder path.";
fi
}
main $#;
I have tried in many ways but couldn't get it the right way.
fun="
mkcdo ()
{
mkdir -p -- \"'$1'\" && cd -P -- \"'$1'\"
}"
echo "$fun" >> ~/.bashrc
What I want is to append this in .bashrc
mkcd ()
{
mkdir -p -- "$1" && cd -P -- "$1"
}
Could that be done? Is there a way in bash like there is in python: r'whatever\you\$write' so that it is completely ignored as simple text?
Using a variable to store a bash function code sounds much of anti-pattern. For multi-line formatted strings, I would recommend using here-doc and quote them to avoid expanding the variable,
cat >> ~/.bashrc << 'EOF'
mkcd () {
mkdir -p -- "$1" && cd -P -- "$1"
}
EOF
Further reading - Bash - Here documents
Alternative1: just use single quotes.
fun='
mkcdo ()
{
mkdir -p -- "$1" && cd -P -- "$1"
}'
echo "$fun" >> ~/.bashrc
Alternative2: escape the $ sign.
fun="
mkcdo ()
{
mkdir -p -- \"\$1\" && cd -P -- \"\$1\"
}"
echo "$fun" >> ~/.bashrc
What I am trying to do is having a config file that controls a very basic backup script.
#Credentials
username="backup.user"
password="****"
from="/mnt"
to="/home/backup"
#Mountpoints
n=1
source="//10.X.X.X/Public"
destination="/mnt/Public"
n=2
source="//10.X.X.X/it"
destination="/mnt/it"
Inside the script itself it looks like this:
#!/bin/bash
#getting variables from the external config file
export $(cat config.ini | grep -v ^# | xargs)
#the command I am trying to achieve
mountpoint[$n]="mount -t cifs -o username=$username,password=$password,ro $source $destination"
#mounts the array of mountpoints defined
for mountpoint in "${mountpoint[#]}";
do
${mountpoint}
done
function currentDate () {
date +%Y%m%d
}
if [ ! -d "$to/$(currentDate)" ] ; then
mkdir "$to/$(currentDate)";
cp --verbose -R "$from/." "$to/$(currentDate)" >> $to/$(currentDate)/fileLog.txt
diff -qr $from $to/$(currentDate) >> $to/$(currentDate)/differencesLog.txt
else
exit
fi
umount -a -t cifs -l /mnt/*
done
I am trying to do this:
Have a set of variables in config for each mountpoint.
A for loop for that will echo the last source and destination as normal because it doesn't know when a certain set of variables is done for "n=1".
How will you guys do that?
Thanks a lot!
# create an array
mountpoint=()
# append to the array
mountpoint+=("item 1")
mountpoint+=("item 2")
# iterate the array
for i in "${mountpoint[#]}"; do
echo "${i}"
done
I'm trying to make a script which asks for a directory path and then creates that directory. I'd like to be able to pass variables to the read builtin so pathnames so I don't have to type out full paths:
function make_dir {
echo "What is the path of the directory to be created?"
read directory
mkdir "$directory"
}
so I'd type:
make_dir
What is the path of the directory to be created?
$HOME/temp_dir
mkdir: cannot create directory `$HOME/temp_dir': No such file or directory
So I'd like to have $HOME expanded into /home/user/ and the script to make the directory /home/user/temp_dir, but I can't seem to get the expansion to work.
If I modify the make_dir function to show_dir below
function show_dir {
echo "What is the path of the directory to be created?"
read directory
echo "The directory is $directory"
}
and I type $HOME/temp_dir and get the following:
make_dir
What is the path of the directory to be created?
$HOME/temp_dir
The directory is $HOME/temp_dir
with no expansion. Any ideas on how to get this to work?
It's a little cumbersome, but one option is to use the -e flag to tell read to use Readline to get the input, then use Readline to expand the line after typing it, but before hitting Enter.
$ read -e directory
$HOME/dir
Now type Meta-Control-e, and Readline will expand the input just as if it were being processed prior to execution as a shell command. (Note that the Meta key is probably Alt or Esc, depending on your terminal setup.)
You are actually making things more difficult by attempting to get the directory with read. Unless you have an absolute requirement to use read, you are better off passing the directory to your function as an argument. For example:
function make_dir {
[ -n "$1" ] || {
printf "\n usage: make_dir <path_to_create>\n\n"
return 1
}
mkdir -p "$1" || {
printf "\n error: unable to create '$1', check permissions\n\n"
}
}
example:
$ function make_dir {
> [ -n "$1" ] || {
> printf "\n usage: make_dir <path_to_create>\n\n"
> return 1
> }
> mkdir -p "$1" || {
> printf "\n error: unable to create '$1', check permissions\n\n"
> }
> }
$ make_dir $HOME/temp_dir
$ ls -al temp_dir
total 8
drwxr-xr-x 2 david david 4096 Nov 26 15:34 .
drwxr-xr-x 76 david david 4096 Nov 26 15:34 ..
$ make_dir
usage: make_dir <path_to_create>
When you pass the directory to your function as an argument instead of using read, you can easily adjust your function to take/create multiple directories as well:
function make_dir {
[ -n "$1" ] || {
printf "\n usage: make_dir <path_to_create> [path2, ..]\n\n"
return 1
}
for i in "$#" ; do
mkdir -p "$i" || {
printf "\n error: unable to create '$i', check permissions\n\n"
}
done
}
example:
$ make_dir temp_dir_{1..3}
$ ls -1d temp_*
temp_dir_1
temp_dir_2
temp_dir_3
change the following line:
mkdir "$directory"
with
eval mkdir -p "$directory"