I want to automatically kick off a build whenever a file changes.
I've used autospec (RSpec) in Ruby and loved that.
How can this be done in bash?
Take a look at incron and inotify-tools.
keywords are inotifywait & inotifywatch commands
After reading replies to other posts, I found a post (now gone), I created this script :-
#!/bin/bash
sha=0
previous_sha=0
update_sha()
{
sha=`ls -lR . | sha1sum`
}
build () {
## Build/make commands here
echo
echo "--> Monitor: Monitoring filesystem... (Press enter to force a build/update)"
}
changed () {
echo "--> Monitor: Files changed, Building..."
build
previous_sha=$sha
}
compare () {
update_sha
if [[ $sha != $previous_sha ]] ; then changed; fi
}
run () {
while true; do
compare
read -s -t 1 && (
echo "--> Monitor: Forced Update..."
build
)
done
}
echo "--> Monitor: Init..."
echo "--> Monitor: Monitoring filesystem... (Press enter to force a build/update)"
run
How about this script? Uses the 'stat' command to get the access time of a file and runs a command whenever there is a change in the access time (whenever file is accessed).
#!/bin/bash
while true
do
ATIME=`stat -c %Z /path/to/the/file.txt`
if [[ "$ATIME" != "$LTIME" ]]
then
echo "RUN COMMNAD"
LTIME=$ATIME
fi
sleep 5
done
If you've entr installed, then in shell you can use the following syntax:
while true; do find src/ | entr -d make build; done
See this example as an improvement upon on Ian Vaughan's answer:
#!/usr/bin/env bash
# script: watch
# author: Mike Smullin <mike#smullindesign.com>
# license: GPLv3
# description:
# watches the given path for changes
# and executes a given command when changes occur
# usage:
# watch <path> <cmd...>
#
path=$1
shift
cmd=$*
sha=0
update_sha() {
sha=`ls -lR --time-style=full-iso $path | sha1sum`
}
update_sha
previous_sha=$sha
build() {
echo -en " building...\n\n"
$cmd
echo -en "\n--> resumed watching."
}
compare() {
update_sha
if [[ $sha != $previous_sha ]] ; then
echo -n "change detected,"
build
previous_sha=$sha
else
echo -n .
fi
}
trap build SIGINT
trap exit SIGQUIT
echo -e "--> Press Ctrl+C to force build, Ctrl+\\ to exit."
echo -en "--> watching \"$path\"."
while true; do
compare
sleep 1
done
Related
I am trying to write a bash script to remove cookies and cache from installed browsers on shared Ubuntu machines. The problem I am facing is in creating a menu where you can select either ALL users or individual users.
I am trying to create a main menu that calls either of the 2 functions (a work in progress) to perform the tasks (I have commented out the commands to run for the meantime).
#!/bin/bash
# Remove Browser cache from Ubuntu 16.04 or Ubuntu 18.04
# Check running as root/sudo
if [ "$EUID" -ne 0 ] ;then
echo -e "Please run with;\nsudo $0"
exit
fi
# Enable extended globbing for the +(...) pattern
shopt -s extglob
## Check Ubuntu version
VERSION=$(lsb_release -d | awk -F":" '/Description/ {print $2}')
if [[ "$VERSION" = *"Ubuntu 18.04"* ]]; then
HOME_DIR="/home/ANT.DOMAIN.COM"
else
[[ "$VERSION" = *"Ubuntu 16.04"* ]]
HOME_DIR="/home/local/ANT"
fi
# Set Colours
RED='\033[1;31m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
NC='\033[0m' # No Color
## Clear Browser Cache for ALL Users
clear_cache_all () {
mapfile -t PROFILES < <(find "$HOME_DIR" -mindepth 1 -maxdepth 1 -type d)
for PRO in "${PROFILES[#]}"
do
# Check FireFox installed
dpkg -s firefox &> /dev/null
if [ $? -eq 0 ]; then
#rm -rf "$PRO"/.mozilla/firefox/*.default/*.sqlite "$PRO"/.mozilla/firefox/*default/sessionstore.js
#rm -rf "$PRO"/.cache/mozilla/firefox/*.default/*
echo -e "FireFox Cookies & Cache Cleared for user ${GREEN}$USERNAME${NC}"
else
echo -e "${YELLOW}FireFox Not Installed...moving on${NC}"
fi
# Check Chromium installed
dpkg -s chromium-browser &> /dev/null
if [ $? -eq 0 ]; then
#rm -rf "$PRO"/.config/chromium/Default/
#rm -rf "$PRO"/.cache/chromium
echo -e "Chromium Cookies & Cache Cleared for user ${GREEN}$USERNAME${NC}"
else
echo -e "${YELLOW}Chromium Not Installed...moving on${NC}"
fi
# Check Chrome installed
dpkg -s google-chrome-stable &> /dev/null
if [ $? -eq 0 ]; then
#rm -rf "$PRO"/.config/google-chrome/Default/
#rm -rf "$PRO"/.cache/google-chrome
echo -e "Google Chrome Cookies & Cache Cleared for user ${GREEN}$USERNAME${NC}"
else
echo -e "${YELLOW}Google Chrome Not Installed...moving on${NC}"
fi
done
}
## Clear Cache for Individual Users
clear_cache_user () {
echo "stuff!"
}
# main menu function
main_menu () {
clear
if [ -d "$HOME_DIR" ]
then
mapfile -t USERS < <(find "$HOME_DIR" -mindepth 1 -maxdepth 1 -type d)
# Get basename for users
USERNAME="${USERS[#]##*/}"
string="#(${USERNAME[0]}"
for((i=1;i<${#USERNAME[#]};i++))
do
string+="|${USERNAME[$i]}"
done
string+=")"
select NAME in "Clear ALL" "${USERNAME[#]}" "Quit"
do
case $NAME in
"Clear ALL")
# Call clear_cache_all Function
clear_cache_all
exit
;;
$string)
# Call clear_cache_user Function
clear_cache_user
;;
"Quit")
exit
;;
*)
echo "Invalid option, please try again";;
esac
done
else
echo -e "${RED}Error: Cannot find home directories...exiting${NC}"
fi
}
### SCRIPT COMMANDS ###
main_menu
Ok, so I can think of two options for your problem. I'll try to follow the names of your variables.
As I can see in your code, you have already put in the variable "string" all the usernames, so my first idea is to use a read and a simple if:
read -P "Insert ALL for all users, the Username for a single user, or Quit to exit: " NAME
if [ $NAME = "ALL" ]
then
clear_cache_all
exit
elif [ $NAME = "Quit" ]
then
echo "Bye!"
exit
else
for i in "${string[#]}"
do
if [ "$i" == "$NAME" ] ; then
clear_cache_user($NAME) #Guessing you'll pass the username as a variable to the function
exit
fi
done
echo "Invalid option, please try again"
fi
The other option is to use the case statement, as you were using. The problem is that case doesn't work easy with arrays, so while it's "case / in", it doesn't mean it's checking if the variable is an element of the array. In case you are forced to use case (or are in love with it), check this two links for some solutions: this one and this one.
Hope this helps! Good luck!
I have a series of 250 bash scripts which need to be executed in parallel. Each script utilizes approximately 1 core so I do not want to execute them all at the same time. I would like to run 10 scripts at the same time and whenever one finishes execute another script.
I recommend parallel, but I'm going to post this monstrosity for the benefit of having people pick it apart and tune it. :)
#! /bin/env bash
## TODO: this documentation block needs to be expanded.
use="
$0 <#procs> <cmdfile>
Pass the number of desired processes to prespawn as the 1st argument.
Pass the command file with the list of tasks you need done.
Command file format:
KEYSTRING:cmdlist
where KEYSTRING will be used as a unique logfile name
and cmdlist is the base command string to be run
KEYSTRING may not contain whitespace of any sort.
Other lines are not allowed, including blanks or comments.
"
die() { echo "$# $use" >&2; exit 1; }
case $# in
2) case "$1" in
*[^0-9]*) die "INVALID #procs '$1'" ;;
esac
declare -i primer="$1" # a countdown of how many processes to pre-spawn
cmdfile="$2"
[[ -r "$cmdfile" ]] || { die "$cmdfile not readable"; }
grep -v : "$cmdfile" || { die "$cmdfile has invalid lines"; }
declare -i lines=$( grep -c : $cmdfile)
if (( lines < primer ))
then die "Note - command lines in $cmdfile ($lines) fewer than requested process chains ($primer)"
fi ;;
*) die ;;
esac >&2
trap 'echo abort $0#$LINENO; use; exit 1' ERR # make sure any error is fatal
trap ': no-op to ignore' HUP # ignore hangups (built-in nohup without explicit i/o redirection)
spawn() {
IFS="$IFS:" read key cmd && [[ "${cmd:-}" ]] || return
echo "$(date) executing '$cmd'; c.f. $key.log" | tee "$key.log"
echo "# autogenerated by $0 $(date)
{ $cmd
spawn
} >> $key.log 2>&1 &
" >| $key.sh
. $key.sh
rm -f $key.sh
return 0
}
# create a command list based on those designators
declare chains=0
while (( primer-- )) # until we've filled the requested quota
do spawn # create a child process
done < $cmdfile
I'm building a script for php-fpm compilation, installation and deployment in ubuntu 14. At one point, I have got to generate another file using this main script. The resulting file is a script and should have all variables BUT one NOT expanded.
So I started with cat << 'EOT' in will of resolving the thing after the file generation with sed. But I find myself in a "logic" blackhole.
As for the EOT quoting beeing an issue for expanding just one variable, the same is for the sed line. I went straight writing the following, then laught at it without even executing it, of course.
sed -i 's/\$PhpBuildVer\/$PhpBuildVer' /etc/init.d/php-$PhpBuildVer-fpm
OR
sed -i "s/\$PhpBuildVer\/$PhpBuildVer" /etc/init.d/php-$PhpBuildVer-fpm
both would fail, while I need the first pattern to be the "$PhpBuildVer" itself and the other one beeing the expanded variable, for instance, 7.1.10.
How would I perform this substituion with either sed or another GNU Linux command?
This is my script, most of the parts have been cut-off as non question related.
#!/bin/bash
PhpBuildVer="7.1.10"
... #removed non relevant parts of the script
cat << 'EOT' >> /etc/init.d/php-$PhpBuildVer-fpm
#! /bin/sh
### BEGIN INIT INFO
# Provides: php-$PhpBuildVer-fpm
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts php-$PhpBuildVer-fpm
# Description: starts the PHP FastCGI Process Manager daemon
### END INIT INFO
php_fpm_BIN=/opt/php-$PhpBuildVer/sbin/php-fpm
php_fpm_CONF=/opt/php-$PhpBuildVer/etc/php-fpm.conf
php_fpm_PID=/opt/php-$PhpBuildVer/var/run/php-fpm.pid
php_opts="--fpm-config $php_fpm_CONF"
wait_for_pid () {
try=0
while test $try -lt 35 ; do
case "$1" in
'created')
if [ -f "$2" ] ; then
try=''
break
fi
;;
'removed')
if [ ! -f "$2" ] ; then
try=''
break
fi
;;
esac
echo -n .
try=`expr $try + 1`
sleep 1
done
}
case "$1" in
start)
echo -n "Starting php-fpm "
$php_fpm_BIN $php_opts
if [ "$?" != 0 ] ; then
echo " failed"
exit 1
fi
wait_for_pid created $php_fpm_PID
if [ -n "$try" ] ; then
echo " failed"
exit 1
else
echo " done"
fi
;;
stop)
echo -n "Gracefully shutting down php-fpm "
if [ ! -r $php_fpm_PID ] ; then
echo "warning, no pid file found - php-fpm is not running ?"
exit 1
fi
kill -QUIT `cat $php_fpm_PID`
wait_for_pid removed $php_fpm_PID
if [ -n "$try" ] ; then
echo " failed. Use force-exit"
exit 1
else
echo " done"
echo " done"
fi
;;
force-quit)
echo -n "Terminating php-fpm "
if [ ! -r $php_fpm_PID ] ; then
echo "warning, no pid file found - php-fpm is not running ?"
exit 1
fi
kill -TERM `cat $php_fpm_PID`
wait_for_pid removed $php_fpm_PID
if [ -n "$try" ] ; then
echo " failed"
exit 1
else
echo " done"
fi
;;
restart)
$0 stop
$0 start
;;
reload)
echo -n "Reload service php-fpm "
if [ ! -r $php_fpm_PID ] ; then
echo "warning, no pid file found - php-fpm is not running ?"
exit 1
fi
kill -USR2 `cat $php_fpm_PID`
echo " done"
;;
*)
echo "Usage: $0 {start|stop|force-quit|restart|reload}"
exit 1
;;
esac
EOF
#Here the variable should be substituted.
chmod 755 /etc/init.d/php-$PhpBuildVer-fpm
... #removed non relevant parts of the script
I am not 100% sure, but I think what you are looking for is:
sed -i 's/\$PhpBuildVer/'"$PhpBuildVer"'/' /etc/init.d/php-$PhpBuildVer-fpm
You can actually put two quoted expressions right next to each other in bash. E.g., echo '12'"34"'56' will output 123456. In this case, the first \$PhpBuildVer is in '' so it can match literally, and the second is in "" so that it will be expanded.
(But maybe you should consider using a template file and php, or (blatant plug)
perlpp* to build the script, rather than inlining all the text into your main script. ;) )
Edit by the way, using cat ... >> rather than cat ... > means you will be appending to the script unless you have rmed it somewhere in the code you didn't show.
Edit 2 If $PhpBuildVer has any characters in it that sed interprets in the replacement text, you might need to escape it:
repl_text="$(sed -e 's/[\/&]/\\&/g' <<<"$PhpBuildVer")"
sed -i 's/\$PhpBuildVer/'"$repl_text"'/' /etc/init.d/php-$PhpBuildVer-fpm
Thanks to this answer by Pianosaurus.
Tested example
I put this in make.sh:
#!/bin/bash
f=42 # The variable we are going to substitute
cat <<'EOT' >"test-$f.sh" # The script we are generating
#!/bin/sh
# Provides: test-$f.sh
echo 'Hello, world!'
EOT
echo "test-$f.sh before substitution is:"
echo "---------"
cat "test-$f.sh"
echo "---------"
sed -i 's/\$f/'"$f"'/' "test-$f.sh" # The substitution, from above
echo "test-$f.sh after substitution is:"
echo "---------"
cat "test-$f.sh"
echo "---------"
The output I get is:
test-42.sh before substitution is:
---------
#!/bin/sh
# Provides: test-$f.sh
echo 'Hello, world!'
---------
(note the literal $f)
test-42.sh after substitution is:
---------
#!/bin/sh
# Provides: test-42.sh
echo 'Hello, world!'
---------
(now the $f is gone, and has been replaced with its value, 42)
perlpp example
Since *I am presently the maintainer of perlpp, I'll give you that example, too :) . In a template file that I called test.template, I put:
#!/bin/sh
# Provides: test-<?= $S{ver} ?>.sh
echo 'Hello, world!'
That was exactly the content of the script I wanted, but with <?= $S{ver} ?> where I wanted to do the substitution. I then ran
perlpp -s ver=\'7.1.10\' test.template
(with escaped quotes to pass them to perl) and got the output:
#!/bin/sh
# Provides: test-7.1.10.sh
echo 'Hello, world!'
:)
Any -s name=\'value\' command-line argument to perlpp creates $S{name}, which you can refer to in the template.
<?= expr ?> prints the value of expression expr
Therefore, <?= $S{name} ?> outputs the value given on the command line for name.
Just break up the heredoc. eg
cat > file << 'EOF'
This line will not be interpolated: $FOO
EOF
cat >> file << EOF
and this line will: $FOO
EOF
If for some reason you do want to used sed as well, don't do it after, just use it instead of cat:
sed 's#foo#bar#g' >> file << EOF
this line's foo is changed by sed, with interpolated $variables
EOF
I want to associate the FTP session with the file descriptor, which would refer to it throughout the script. Including cycles.
For example something like this. But it did not get to do it.
#!/bin/bash
#start of script
exec {ftpdescriptor}<> >(lftp -u $ftpuser,$ftppass $ftpip/$ftptd)
# code
(echo "ls" 1>&"$ftpdecriptor")> myanswer
# code
echo "bye" 1>&"$ftpdecriptor"
exec {ftpdescriptor}>&-
exit 0
# end of script
It works, but the answer is always going to stdout..
Solved the problem so
# start of script
ftpout=$(mktemp)
$timetowaitftp
exec {ftpin}> >(lftp -u $ftpuser,$ftppass $ftpip/$ftptd > $ftpout)
printf >&$ftpin "set net:timeout 10\n"
function ftpio {
:>$ftpout
printf >&$ftpin "$1\n"
i=0
while [ ! $2 ] && [ ! -s $ftpout ] && [ $i -lt 10 ]; do
# echo "waiting answer from ftp 1 sec.."
sleep 1;
let i=i+1
done
}
# code
ftpio "cd /modx" "nowait" # no output of cd command.
ftpio "ls"
cat $ftpout
sleep 15
ftpio "pwd"
cat $ftpout
#ftpio "put /var/www/vhosts/modx/backups/20160121.113318.tar.gz" "nowait" # 12Gb
#ftpio "put /var/www/vhosts/modx/backups/20150930.092338.tar.gz" "nowait" # 800mb
# /code
# end of script
printf 1>&"$ftpin" "bye\n"
exec {ftpin}>&-
rm $ftpout
ftpin - named descriptor
ftpout - temp file with last answer of ftp
I am new to php daemons. I am using the below script to fire Daemon.php script. But i am getting error while executing this below bash script via shell
The error is,
exit: 0RETVAL=0: numeric argument required
Please help me resolve this error
#!/bin/bash
#
# /etc/init.d/Daemon
#
# Starts the at daemon
#
# chkconfig: 345 95 5
# description: Runs the demonstration daemon.
# processname: Daemon
# Source function library.
#. /etc/init.d/functions
#startup values
log=/var/log/Daemon.log
#verify that the executable exists
test -x /home/godlikemouse/Daemon.php || exit 0RETVAL=0
#
# Set prog, proc and bin variables.
#
prog="Daemon"
proc=/var/lock/subsys/Daemon
bin=/home/godlikemouse/Daemon.php
start() {
# Check if Daemon is already running
if [ ! -f $proc ]; then
echo -n $"Starting $prog: "
daemon $bin --log=$log
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $proc
echo
fi
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc $bin
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $proc
echo
return $RETVAL
}
restart() {
stop
start
}
reload() {
restart
}
status_at() {
status $bin
}
case "$1" in
start)
start
;;
stop)
stop
;;
reload|restart)
restart
;;
condrestart)
if [ -f $proc ]; then
restart
fi
;;
status)
status_at
;;
*)
echo $"Usage: $0 {start|stop|restart|condrestart|status}"
exit 1
esac
exit $?
exit $RETVAL
This line produces the error:
test -x /home/godlikemouse/Daemon.php || exit 0RETVAL=0
If you want to set the the value of RETVAL to 0 you first need to remove the 0 as you can not have variables that start with a number.
Then you remove the value set from the second statement so it will exit in case Daemon.php does not exist.
test -x /home/godlikemouse/Daemon.php || exit
You can also remove the 2 empty echo statements inside the start and stop functions as the do nothing.
There are also errors in the case statement. You need to quote the case options and can remove the last exit block as the exit $? will trigger the exit before.
case "$1" in
"start")
start
;;
"stop")
stop
;;
"reload"|"restart")
restart
;;
"condrestart")
if [ -f $proc ]; then
restart
fi
;;
"status")
status_at
;;
There is several syntax and logic errors in this script presented. To highlight several:
echo $"Usage (should be just echo "Usage ..." since the string in ".." is not a variable
Double exit statements, the second one for $RETVAL is never ran.
exit 0RETVAL is not the same as exit $RETVAL, and one should just be using exit 1 instead to denote an error, exit 0 means the script ran correctly
$prog is defined but never used
test -x is to check for executable bit enabled in the given path. test -f is safer when testing for a file, test -d safer for testing directories, and test -L is safer when testing symlinks. Combine the test -f and test -x to ensure there is no race conditions or worst. (example: (test -f /home/godlikemouse/Daemon.php && test -x /home/godlikemouse/Daemon.php) || exit 1))
Further details on creating sysv init scripts can be read at http://refspecs.linuxbase.org/LSB_3.0.0/LSB-generic/LSB-generic/iniscrptact.html and bash scripting can be read at http://www.tldp.org/LDP/abs/html/index.html. It is strongly encouraged to learn both before writing system control programs such as init scripts.