Generate unique filename for fswebcam - shell

I have fswebcam installed in my ubuntu. I want the fswebcam to provide output as img1, img2 (if img1 is there), img3, img4... etc
I tried:
sudo fswebcam img
It stores the files as img but replaces the existing one instead of storing as img2.
Is there any specific type of unix command to store the filename as I specified?

Or you could just use its built in strftime capability to generate each file with the current time in the filename
--save pic%Y-%m-%d_%H:%M:%S.jpg

I would also like to know that specific command if it exists. Meanwhile, I also needed to do that, and I used a workaround as follows (adapted to your needs):
f() {
PREFIX="./img"
FILES=$(ls $PREFIX* 2> /dev/null)
LAST=$(sort -n <<<"${FILES//$PREFIX}" | tail -n1)
echo $PREFIX$((LAST+1))
}
FILES contains al the filenames separated by \n.
LAST will have nothing, or the max number after the $PREFIX.
Finally, the function echo'es the last filename incremented by 1.
So, once you have defined f (or a more significant name), you can call your command like this:
sudo fswebcam $(f)
Example
$ ls
img1 img10 img11 img2 img3 img4 img5 img6 img7 img8 img9
$ echo $(f) # here I'm using "echo" instead of "sudo fswebcam"
./img12
f step by step
$ FILES=$(ls $PREFIX* 2> /dev/null)
$ cat <<<"$FILES"
./img1
./img10
./img11
./img2
./img3
./img4
./img5
./img6
./img7
./img8
./img9
$ LAST=$(sort -n <<<"${FILES//$PREFIX}" | tail -n1)
$ echo $LAST
11
$ echo $PREFIX$((LAST+1))
./img12

location="your location directory"
cd $location
ls
fname='filename'
i=1
j=1
sudo fswebcam $fname
if [ -f $fname ]
then
echo "exist"
while [ -f $fname$i ]
do
echo $fname$i
i=$((i + j))
done
mv $fname $fname$i
else
echo "oops not found"
fi

Related

Bash complete function - Separating completion parts with character other than space

I've written a bash completion script to essentially do file/directory completion, but using . as the separator instead of /. However, it's not behaving as I expect it to.
Before I dive further, does anyone know of any options for this, or something that's already been written that can do this? The motivation for this is to enable completion when calling python with the -m flag. It seems crazy that this doesn't exist yet, but I was unable to find anything relevant.
My issue is that bash doesn't recognize . as a separator for completion options, and won't show the next options until I add an additional space to the end of the current command.
Here's a few concrete examples, given this directory structure.
/module
/script1.py
/script2.py
For instance, when I use the ls command, it works like this
$ ls mo<TAB>
$ ls module/<TAB><TAB>
script1.py script2.py
However, with my function, it's working like this:
$ python -m mod<TAB>
$ python -m module.<TAB><TAB>
module.
So instead of showing the next entries, it just shows the finished string again. However, if I add a space, it then works, but I don't want it to include the space:
$ python -m mod<TAB>
$ python -m module. <TAB><TAB> # (note the space here after the dot)
script1 script2 # (Note, I'm intentionally removing the file extension here).
I'd like the completion to act just like the bottom example, except not be forced to include the space to go to the next set of options
I've got about 50 tabs open and I've tried a bunch of recommendations, but nothing seems to be able to solve this how I'd like. There are a few other caveats here that would take a lot of time to go through, so I'm happy to expand on any other points if I've skipped something important. I've attached my code below, any help would be greatly appreciated. Thanks!
#!/bin/bash
_python_target() {
local cur opts cur_path
# Retrieving the current typed argument
cur="${COMP_WORDS[COMP_CWORD]}"
# Preparing an array to store available list for completions
# COMREPLY will be checked to suggest the list
COMPREPLY=()
# Here, we'll only handle the case of "-m"
# Hence, the classic autocompletion is disabled
# (ie COMREPLY stays an empty array)
if [[ "${COMP_WORDS[1]}" != "-m" ]]
then
return 0
fi
# add each path component to the current path to check for additional files
cur_path=""
for word in ${COMP_WORDS[#]:2:COMP_CWORD-2}; do
path_component=$(echo ${word} | sed 's/\./\//g')
cur_path="${cur_path}${path_component}"
done
cur_path="./${cur_path}"
if [[ ! -f "$cur_path" && ! -d "$cur_path" ]]; then
return 0
fi
# this is not very pretty, but it works. Open to comments on this too
file_opts="$(find ${cur_path} -name "*.py" -type f -maxdepth 1 -print0 | xargs -0 basename -a | sed 's/\.[^.]*$//')"
dir_opts="$(find ${cur_path} ! -path ${cur_path} -type d -maxdepth 1 -print0 | xargs -0 basename -a | xargs -I {} echo {}.)"
opts="${file_opts} ${dir_opts}"
# We store the whole list by invoking "compgen" and filling
# COMREPLY with its output content.
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
[[ $COMPREPLY == *\. ]] && compopt -o nospace
}
complete -F _python_target python
Here's a draft example:
_python_target()
{
local cmd=$1 cur=$2 pre=$3
if [[ $pre != -m ]]; then
return
fi
local cur_slash=${cur//./\/}
local i arr arr2
arr=( $( compgen -f "$cur_slash" ) )
arr2=()
for i in "${arr[#]}"; do
if [[ -d $i ]]; then
arr2+=( "$i/" )
elif [[ $i == *.py ]]; then
arr2+=( "${i%.py}" )
fi
done
arr2=( "${arr2[#]//\//.}" )
COMPREPLY=( $( compgen -W "${arr2[*]}" -- "$cur" ) )
}
complete -o nospace -F _python_target python
Try with the python-2.7.18 source code directory:

Bash simple script copying files to specific folder + renaming to todays effective date

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.

how to create incremental files with version in shell

how to create files dynamically when i run shell script.
Intially in the /tmp folder i need to check if file like(CFT_AH-120_v1.txt) exist in tmp folder else create CFT_AH-120_v1.txt. Next time when i run shell script it should create CFT_AH-120_v2.txt and in each run it should increment the version number of the file.
In tmp folder i should have files like
CFT_AH-120_v1.txt
CFT_AH-120_v2.txt
CFT_AH-120_v3.txt
i will get CFT_AH-120 from variable dynamically.
#!/bin/bash
export filename
temp=$(find CFT_AH-120-V* | sort -V | tail -1)
if [ -e $temp ]
then echo "ok"
echo $temp
fname="${temp%.*}"
echo $fname
temp1="${temp%[[:digit:]]*}$((${temp##*[[:alpha:]]} + 1))"
echo $temp1
touch $temp1 ".txt"
else
touch CFT_AH-120-V1.txt
echo "nok"
fi
I am not sure the exact requirement for you. As per my understanding this is a simple way of approach.
#!/bin/bash
file_temp=$(find . -name "CFT_AH-120-V*" -type f | sort -V | tail -1)
echo $temp
if [ -z "$temp" ]; then
echo "File not found!"
else
num_temp=$(echo $temp | cut -d '-' -f3- | sed 's/V//')
num_value_incr=$(expr $new_temp + 1)
touch "CFT_AH-120-V$num_value_incr"
echo "New file created!"
fi
Note: This code search for the largest value of the "V"....number.... (eg.***V120) and increment based on that value and also the intermediate number wont be increment. If you need the missing numbers to be created, then the logic for this code needs to be changed.
Hope this might help you!

Comparing an existing file with the result of a heavy process using named pipes

I'm trying to figure out a way to compare an existing file with the result of a process (a heavy one, not to be repeated) and clobber the existing file with the result of that process without having to write it in a temp file (it would be a large temp file, about the same size of the existing file: let's try to be efficient and not take twice space it should).
I would like to replace the normal file /tmp/replace_with_that (see below) with a fifo, but of course doing so with the code below would just lock up the script, since the /tmp/replace_with_that fifo cannot be read before comparing the existing file with the named pipe /tmp/test_against_this
#!/bin/bash
mkfifo /tmp/test_against_this
: > /tmp/replace_with_that
echo 'A B C D' >/some/existing/file
{
#A very heavy process not to repeat;
#Solved: we used a named pipe.
#Its large output should not be sent to a file
#To solve: using this code, we write the output to a regular file
for LETTER in "A B C D E"
do
echo $LETTER
done
} | tee /tmp/test_against_this /tmp/replace_with_that >/dev/null &
if cmp -s /some/existing/file /tmp/test_against_this
then
echo Exact copy
#Don't do a thing to /some/existing/file
else
echo Differs
#Clobber /some/existing/file with /tmp/replace_with_that
cat /tmp/replace_with_that >/some/existing/file
fi
rm -f /tmp/test_against_this
rm -f /tmp/replace_with_that
I think I would recommend a different approach:
Generate an MD5/SHA1/SHA256/whatever hash of the existing file
Run your heavy process and replace the output file
Generate a hash of the new file
If the hashes match, the files were the same; if not, the new file is different
Just for completeness, my answer (wanted to explore the use of pipes):
Was trying to find a way to compare on the fly a stream and an existing file, without overwriting the existing file unnecessarily (leaving it as is if stream and file are exact copies), and without creating sometimes big temp files (the product of a a heavy process like mysqldump for instance). The solution had to rely on pipes only (named and anonymous), and maybe a few very small temp files.
The checksum solution suggested by twalberg is just fine, but md5sum calls on large files are processor intensive (and processing time lengthens linearly with file size). cmp is faster.
Example call of the function listed below:
#!/bin/bash
mkfifo /tmp/fifo
mysqldump --skip-comments $HOST $USER $PASSWORD $DB >/tmp/fifo &
create_or_replace /some/existing/dump /tmp/fifo
#This also works, but depending on the anonymous fifo setup, seems less robust
create_or_replace /some/existing/dump <(mysqldump --skip-comments $HOST $USER $PASSWORD $DB)
The functions:
#!/bin/bash
checkdiff(){
local originalfilepath="$1"
local differs="$2"
local streamsize="$3"
local timeoutseconds="$4"
local originalfilesize=$(stat -c '%s' "$originalfilepath")
local starttime
local stoptime
#Hackish: we can't know for sure when the wc subprocess will have produced the streamsize file
starttime=$(date +%s)
stoptime=$(( $starttime + $timeoutseconds ))
while ([[ ! -f "$streamsize" ]] && (( $stoptime > $(date +%s) ))); do :; done;
if ([[ ! -f "$streamsize" ]] || (( $originalfilesize == $(cat "$streamsize" | head -1) )))
then
#Using streams that were exact copies of files to compare with,
#on average, with just a few test runs:
#diff slowest, md5sum 2% faster than diff, and cmp method 5% faster than md5sum
#Did not test, but on large unequal files,
#cmp method should be way ahead of the 2 other methods
#since equal files is the worst case scenario for cmp
#diff -q --speed-large-files <(sort "$originalfilepath") <(sort -) >"$differs"
#( [[ $(md5sum "$originalfilepath" | cut -b-32) = $(md5sum - | cut -b-32) ]] && : || echo -n '1' ) >"$differs"
( cmp -s "$originalfilepath" - && : || echo -n '1' ) >"$differs"
else
echo -n '1' >"$differs"
fi
}
create_or_replace(){
local originalfilepath="$1"
local newfilepath="$2" #Should be a pipe, but could be a regular file
local differs="$originalfilepath.differs"
local streamsize="$originalfilepath.size"
local timeoutseconds=30
local starttime
local stoptime
if [[ -f "$originalfilepath" ]]
then
#Cleanup
[[ -f "$differs" ]] && rm -f "$differs"
[[ -f "$streamsize" ]] && rm -f "$streamsize"
#cat the pipe, get its size, check for differences between the stream and the file and pipe the stream into the original file if all checks show a diff
cat "$newfilepath" |
tee >(wc -m - | cut -f1 -d' ' >"$streamsize") >(checkdiff "$originalfilepath" "$differs" "$streamsize" "$timeoutseconds") | {
#Hackish: we can't know for sure when the checkdiff subprocess will have produced the differs file
starttime=$(date +%s)
stoptime=$(( $starttime + $timeoutseconds ))
while ([[ ! -f "$differs" ]] && (( $stoptime > $(date +%s) ))); do :; done;
[[ ! -f "$differs" ]] || [[ ! -z $(cat "$differs" | head -1) ]] && cat - >"$originalfilepath"
}
#Cleanup
[[ -f "$differs" ]] && rm -f "$differs"
[[ -f "$streamsize" ]] && rm -f "$streamsize"
else
cat "$newfilepath" >"$originalfilepath"
fi
}

bash shell script to run imgcmp on two JPGs and store 'different' ones

I've got an IP camera that ftps files to a directory on my SuSE server.
I'm trying to write a shell script to do the following:
for every file in a directory;
use image compare to check this file against the next one
store the output in a file or variable.
if the next file is different then
copy the original to another folder
else
delete the original
end for
Running the following at the prompt generates this:
myserver:/uploads # imgcmp -f img_01.jpg -F img_02.jpg -m rmse > value.txt
myserver:/uploads # cat value.txt
5.559730
5.276747
6.256132
myserver:/uploads #
I know there's loads wrong with the code, the main issue I've got is with executing imgcmp from the script and extracting a value from it, so please point out the obvious as it may not be to me.
FILES=/uploads/img*
declare -i value
declare -i result
value = 10
shopt -s nullglob
# no idea what the above even does #
# IFS=.
# attempt to read the floating point number from imgcmp & make it an integer
for f in $FILES
do
echo "doing stuff w/ $f"
imgcmp -f 4f -F 4f+1 -m rmse > value.txt
# doesn't seem to find the files from the variables #
result= ( $(<value.txt) )
if [ $result > $value ] ; then
echo 'different';
# and copy it off to another directory #
else
echo 'same'
# and delete it #
fi
if $f+1 = null; then
break;
fi
done
when running the above, I get an error cannot open /uploads/img_023.jpg+1
and doing a cat of value.txt shows nothing, so all the files show as being the same.
I know where the issues are, but I've got no idea what I should actually be doing to extract the output of imgcmp (run from within a script) and then get it into a variable that I can compare it with.
FILES=/uploads/*
current=
for f in $FILES; do
if [ -z "$current" ]; then
current="$f"
continue
fi
next="$f"
echo "<> Comparing $current against $next"
## imgcmp will return non-0 if images cannot be compared
## and print an explanation message to stderr;
if result=$(imgcmp -f $current -F $next -m rmse); then
echo "comparison result: " $result
## Checking whether the first value returned
## is greater than 10
if [ "$(echo "$result" | awk '$1 > 10 {print "different"}')" = "different" ]; then
echo 'different';
# cp -v $current /some/other/folder/
else
echo 'same'
# rm -v $current
fi
else
## images cannot be compared... different dimensions / components / ...
echo 'wholly different'
# cp -v $current /some/other/folder/
fi
current="$next"
done

Resources