I have the following code:
#!/bin/bash -x
# Arguments:
# $1 - command to run
# $2 - time limit (in milliseconds)
# $3 - memory limit (in kilobytes)
# NOTE TO SELF: $! is the pid of last process
# NOTE TO SELF: Appending & creates new process
dir=$(mktemp -d)
ulimit -m $3
{ $1 ; "$?" > "$dir/retc" } &
pid=$!
./sleep.pl $2
if [ ps -p $pid > /dev/null ]
then
kill -9 $pid
echo "0Time Limit Exceeded"
else
echo "NAH"
ret=$(cat "$dir/retc")
if [ $ret = 9 ]
then
echo "0Memory Limit Exceeded"
else
if [ $ret = 0 ]
then
echo "1" # If it only returns one then it must be passed through final phase of verifying if result is correct
else
echo "0Received signal $ret"
fi
fi
fi
rm -r $dir
exit 0
However, it returns an error "Unexpected end of file", without executing anything in the if/else blocks.
You missed the semicolon, the redirection is wrong also:
{ $1 ; "$?"; } >"$dir/retc" &
From bash man: list must be terminated with a newline or semicolon
Also:
if [ ps -p $pid > /dev/null ]
Should be:
if [[ $(ps -p $pid >/dev/null) -eq 0 ]]
I'd recommend using () over {} to explicitly show that you're wanting to summon a subshell. Also I think you missed echo to show the value of $?.
( "$1"; echo "$?" > "$dir/etc"; ) &
With () you wouldn't need to add a semicolon in the end unlike with {} however it's still a good practice.
Related
Context
I finally fixed my issue to have stdout & stderr to screen and a file plus having a separate file for the errors. See: Output stdout and stderr to file and screen and stderr to file in a limited environment
Issue
The problem is the ordering of lines.
Some times it is OK:
FIRST
ERROR
LAST
ERROR2
LAST2
Some times the errors are at the end:
FIRST
LAST
LAST2
ERROR
ERROR2
I can't figure out why (except maybe a semaphore underneath that... but... not sure. And if it is the case, there is no solution exception adding line numbers to each echo).
Part of the code where the problem occurs
{ "$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 ; } 3>&1 | tee -a "$logPath/$logFileName.log" &
Full testable code
m=0
declare -a mainArgs
if [ ! "$#" = "0" ]; then
for arg in "$#"; do
mainArgs[$m]=$arg
m=$(($m + 1))
done
fi
function containsElement()
# $1 string to find
# $2 array to search in
# return 0 if there is a match, otherwise 1
{
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
function hasMainArg()
# $1 string to find
# return 0 if there is a match, otherwise 1
{
local match="$1"
containsElement "$1" "${mainArgs[#]}"
return $?
}
function activateLogs()
# $1 = logOutput: What is the output for logs: SCREEN, DISK, BOTH. Default is DISK. Optional parameter.
{
local logOutput=$1
if [ "$logOutput" != "SCREEN" ] && [ "$logOutput" != "BOTH" ]; then
logOutput="DISK"
fi
if [ "$logOutput" = "SCREEN" ]; then
echo "Logs will only be output to screen"
return
fi
hasMainArg "--force-log"
local forceLog=$?
local isFileDescriptor3Exist=$(command 2>/dev/null >&3 && echo "Y")
if [ "$isFileDescriptor3Exist" = "Y" ]; then
echo "Logs are configured"
elif [ "$forceLog" = "1" ] && ([ ! -t 1 ] || [ ! -t 2 ]); then
# Use external file descriptor if they are set except if having "--force-log"
echo "Logs are configured externally"
else
echo "Relaunching with logs files"
local logPath="logs"
if [ ! -d $logPath ]; then mkdir $logPath; fi
local logFileName=$(basename "$0")"."$(date +%Y-%m-%d.%k-%M-%S)
exec 4<> "$logPath/$logFileName.log" # File descriptor created only to get the underlying file in any output option
if [ "$logOutput" = "DISK" ]; then
# FROM: https://stackoverflow.com/a/45426547/214898
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
else
# FROM: https://stackoverflow.com/a/70790574/214898
{ "$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 ; } 3>&1 | tee -a "$logPath/$logFileName.log" &
fi
exit
fi
}
#activateLogs "DISK"
#activateLogs "SCREEN"
activateLogs "BOTH"
echo "FIRST"
echo "ERROR" >&2
echo "LAST"
echo "ERROR2" >&2
echo "LAST2"
Stdout 1 and errout 2 pass by different file descriptors. If is very busy then very close calls like these can get mixed up.
You could use sleep 1 between your calls which would give the system time to process each call in order, but would slow down your script.
You can sleep less than a second : for example sleep 0.5.
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 can't get my bash script (a logging file) to detect any other exit code other than 0, so the count for failed commands isn't being incremented, but the successes is incremented regardless of whether the command failed or succeeded.
Here is the code:
#!/bin/bash
#Script for Homework 8
#Created by Greg Kendall on 5/10/2016
file=$$.cmd
signal() {
rm -f $file
echo
echo "User Aborted by Control-C"
exit
}
trap signal 2
i=0
success=0
fail=0
commands=0
read -p "$(pwd)$" "command"
while [ "$command" != 'exit' ]
do
$command
((i++))
echo $i: "$command" >> $file
if [ "$?" -eq 0 ]
then
((success++))
((commands++))
else
((fail++))
((commands++))
fi
read -p "$(pwd)" "command"
done
if [ "$command" == 'exit' ]
then
rm -f $file
echo commands:$commands "(successes:$success, failures:$fail)"
fi
Any help would be greatly appreciated. Thanks!
That's because echo $i: "$command" is succeeding always.
The exit status $? in if [ "$?" -eq 0 ] is actually the exit status of echo, the command that is run immediately before the checking.
So do the test immediate after the command:
$command
if [ "$?" -eq 0 ]
and use echo elsewhere
Or if you prefer you don't need the $? check at all, you can run the command and check status within if alone:
if $command; then .....; else ....; fi
If you do not want to get the STDOUT and STDERR:
if $command &>/dev/null; then .....; else ....; fi
** Note that, as #Charles Duffy mentioned in the comment, you should not run command(s) from variables.
Your code is correctly counting the number of times that the echo $i: "$command" command fails. I presume that you would prefer to count the number of times that $command fails. In that case, replace:
$command
((i++))
echo $i: "$command" >> $file
if [ "$?" -eq 0 ]
With:
$command
code=$?
((i++))
echo $i: "$command" >> $file
if [ "$code" -eq 0 ]
Since $? captures the exit code of the previous command, it should be placed immediately after the command whose code we want to capture.
Improvement
To make sure that the value of $? is captured before any other command is run, Charles Duffy suggests placing the assignment on the same line as the command like so:
$command; code=$?
((i++))
echo $i: "$command" >> $file
if [ "$code" -eq 0 ]
This should make it less likely that any future changes to the code would separate the command from the capture of the value of $?.
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.
i have a bash script that shows "Segment Violation" on line
sp-sc-auth "${sopUrl}" 8809 8908 > /dev/null &
but when sp-sc-auth is executed from terminal works fine
I set:
set -o pipefail
set -o errexit
set -o xtrace
set -o nounset
end script continue executing but throws that "Segment Violation" error...
System is a debian 64 bits
Thanks in advance
Regars
The ugly code:
#!/usr/bin/env bash
# Init
set -o pipefail
set -o errexit
#set -o xtrace
set -o nounset
__DIR__="$(cd "$(dirname "${0}")"; echo $(pwd))"
__BASE__="$(basename "${0}")"
__FILE__="${__DIR__}/${__BASE__}"
ARG1="${1:-Undefined}"
display_usage() {
scriptName=$(basename $0)
echo -e "Uso:\n "${scriptName}" [6,7,8,9,10 o 12]"
echo "Sin especificar el canal, búsqueda de retransmisiones"
}
parse_arenavision() {
url="http://www.arenavision.in/agenda"
if ! av=$(curl -s "${url}");then
echo "Sin conexión"
exit 1
fi
started="off"
declare -a _list
element=""
while read line
do
if [[ $line =~ (([0-9][0-9]+/[0-9]+/[0-9]+.*)) ]]; then
element=$(echo "${BASH_REMATCH[0]}" | sed -r 's#CET|AV([^6789]|1[02])##g; s#<br />##g; s#//|&.*;##g; s#/\s*$##g; s#INGLATERRA/PREMIER LEAGUE#PREMIER#g; s#ITALIA/SERIE A#SERIE A#g; s#ITALIA/SERIE A#SERIE A#g;' | tr -dc '[:print:]')
if [[ "${element}" =~ (.*AV[6789]|.*AV10|.*AV12) ]]; then
_list+=("${element}")
fi
started="on"
else
if [[ ${started} == "on" ]]; then
break
fi
fi
done <<< "${av}"
for i in "${_list[#]}"; do
if [[ "${i}" =~ (.*BALONCESTO.*) ]]; then
echo -e "\e[92m${i}\e[0m"
elif [[ "${i}" =~ (.*LIGA BBVA.*) ]]; then
echo -e "\e[37m${i}\e[0m"
else
echo "${i}"
fi
done
}
case $ARG1 in
"Undefined" )
parse_arenavision
exit 0
;;
[6789] )
page="${ARG1}"
;;
10 )
page="${ARG1}"
;;
* )
display_usage
exit 1
;;
esac
# Delete "zombies"
if pgrep -f "sp-sc"
then
kill -9 `pgrep -f "sp-sc-auth"`
fi
url="http://www.arenavision.in/arenavision$page"
# Get url content and url sop
if ! content=$(curl -s "${url}");then
echo "Sin conexión"
fi
if [[ $content =~ (sop://([A-Za-z0-9_]+|\.)+:[0-9]+) ]]; then
sopUrl=${BASH_REMATCH[1]}
else
echo "No se ha encontrado la url"
exit 1
fi
# Connect ArenaVision 1
children=""
trap 'kill $children 1>/dev/null 2>&1; exit 143' EXIT
sp-sc-auth "${sopUrl}" 8809 8908 > /dev/null &
children="$!"
# Check if exists
line='[ ]'
for i in {0..15}
do
replace="${line/ /#}"
line=$replace
echo -ne "Comprobando sopcast ${replace}" \\r
sleep 1
done
echo -ne "\033[2K"
if ! kill -0 "${children}" 1>/dev/null 2>&1; then
echo "Sin emisión"
exit 1
else
echo -ne "Comprobando sopcast [ OK ]" \\r
echo
fi
# Open VLC player
line='[ ]'
for i in {0..25}
do
replace="${line/ /#}"
line=$replace
echo -ne "Cargando reproductor ${replace}" \\r
sleep 1
done
if ! kill -0 "${children}" 1>/dev/null 2>&1; then
echo "Fallo en recepción"
exit 1
else
vlc http://localhost:8908/tv.asf 1>/dev/null 2>&1
echo -ne "\033[2K"
fi
exit 0
errexit cannot work on programs run in the background, so this is unsurprising -- the inline command is simply starting a background process, and that (starting a background process) succeeds, even if the process itself subsequently fails.
If you call wait $! subsequently, then errexit will be able to take effect, as the wait call will exit with the exit status of the program itself. (Of course, if you can call wait $!, then this raises the question of why you were backgrounding the initial program to start with).
If you always want to kill the parent script if the child fails, you can do this instead:
(sp-sc-auth "$sopUrl" 8809 8908 >/dev/null || kill $$) &
$$ evaluates to the PID of the parent shell, not the subshell, so this will act accordingly.
As for the segfault itself, "program X segfaults" is a question too vague to be addressed here. To even start debugging that, you'd need to collect the core dump created on its failure (enabling cores if necessary), install debug symbols for sopcast, and use gdb to collect a stack trace from the core file created on failure.