Ksh syntax error '=~' - bash

read -p "The Process running for "$days" days continuously OK to kill this process (y/N)? " -u 4 ok
[[ "${ok}" =~ y ]] || continue
echo "Killing $pid"
kill -HUP "$pid"
fi
This is the snippet of my script ,when i am executing this it shows like
`=~' is not expected.
How to resolve it?

I'm guessing your shebang line has #!/bin/sh and so you don't have access to the full ksh syntax. If you do, ksh93 does appear to support [[ string =~ regex ]] syntax, so there's something here which doesn't add up right.
Either way, there is a construct which works just as well in classic Bourne shell which you can use instead, with the added bonus that your script will be compatible to systems where ksh is not available.
You use read -p <prompt> but that is a Bashism; the -p option to read has a quite different meaning in ksh93.
printf 'Process ran for %i days continuously, OK to kill this? ' "$days"
read -u 4 ok
case $ok in [Yy]* ) ;; *) continue ;; esac
echo "Killing $pid"
kill -HUP "$pid"
Your code looked for y anywhere in the input but I restricted that to only examine the first character.
(Your code had erratic indentation and an unpaired fi which I omitted.)

Your ? is 'how to resolve' - tripleee's suggestion looks like a solution - simplify the code - try:
if [[ "${ok} == "y" ]]
I tried copying your code snippet and I get a different error. Time for D&C - simple ksh93 script testing '=~'

Related

Reliable way to require only bash shell in script

I wonder if there are any reliable methods (cross-shell compatible) to require bash as shell interpreter for my script.
For example, I have shell script that can be run only with bash interpreter. Despite of #!/usr/bin/bash at the beginning of my script some smart user/hacker can run it directly with another shell: $ csh script.sh
This can lead to unwanted consequences.
I already thought about testing echo $0 output and exiting with error code but syntax for if statements (as long as for another conditional statements) is different among various shell interpreters. Testing directly for $BASH_VERSION variable is unreliable due to the same limitations.
Are there any cross-shell compatible and reliable way to determine current interpreter?
Thank you!
EDIT: as for now I have the following basic check for compatibility:
### error codes
E_NOTABASH=1
E_OLD_BASH=2
# perform some checks
if [ -z "$BASH_VERSION" ]
then
echo -e "ERROR: this script support only BASH interpreter! Exiting" >&2
exit $E_NOTABASH
fi
if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]
then
echo -e "ERROR: this script needs BASH 4.0 or greater! Your current version is $BASH_VERSION. Exiting" >&2
exit $E_OLD_BASH
fi
Not entirely sure I understand the scope of the question.
A #! /usr/bin/env bash shebang will fail if there's no bash, but to keep it from being explicitly parsed by another shell, um...
How about -
case "$BASH_VERSION" in
4.*) : bash version 4+ so ok ;;
*) echo "please run only with bash v4+. Aborting."
exit 1 ;;
esac
If the syntax works, it is either right or hacked.
If it crashes, you're good. :)
you could check for the parent process id, command respectively
pstree -p $$ | grep -m 1 -oE '^[^\(]+'
or
ps $(ps -o ppid=$$)

SoF2 shell script not running

I've got the following code in my shell script:
SERVER=`ps -ef | grep -v grep | grep -c sof2ded`
if ["$SERVER" != "0"]; then
echo "Already Running, exiting"
exit
else
echo "Starting up the server..."
cd /home/sof2/
/home/sof2/crons/start.sh > /dev/null 2>&1
fi
I did chmod a+x status.sh
Now I try to run the script but it's returning this error:
./status.sh: line 5: [1: command not found
Starting up the server...
Any help would be greatly appreciated.
Could you please try changing a few things in your script as follows and let me know if that helps you?(changed back-tick to $ and changed [ to [[ in code)
SERVER=$(ps -ef | grep -v grep | grep -c sof2ded)
if [[ "$SERVER" -eq 0 ]]; then
echo "Already Running, exiting"
exit
else
echo "Starting up the server..."
cd /home/sof2/
/home/sof2/crons/start.sh > /dev/null 2>&1
fi
The problem is with the test command. "But", I hear you say, "I am not using the test command". Yes you are, it is also known as [.
if statement syntax is if command. The brackets are not part of if syntax.
Commands have arguments separated (tokenized) by whitespace, so:
[ "$SERVER" != "0" ]
The whitespace is needed because the command is [ and then there are 4 arguments passed to it (the last one must be ]).
A more robust way of comparing numerics is to use double parentheses,
(( SERVER == 0 ))
Notice that you don't need the $ or the quotes around SERVER. Also the spacing is less important, but useful for readability.
[[ is used for comparing text patterns.
As a comment, backticks ` ` are considered deprecated because they are difficult to read, they are replaced with $( ... ).

Abort bash script if git pull fails [duplicate]

I have a Bash shell script that invokes a number of commands.
I would like to have the shell script automatically exit with a return value of 1 if any of the commands return a non-zero value.
Is this possible without explicitly checking the result of each command?
For example,
dosomething1
if [[ $? -ne 0 ]]; then
exit 1
fi
dosomething2
if [[ $? -ne 0 ]]; then
exit 1
fi
Add this to the beginning of the script:
set -e
This will cause the shell to exit immediately if a simple command exits with a nonzero exit value. A simple command is any command not part of an if, while, or until test, or part of an && or || list.
See the bash manual on the "set" internal command for more details.
It's really annoying to have a script stubbornly continue when something fails in the middle and breaks assumptions for the rest of the script. I personally start almost all portable shell scripts with set -e.
If I'm working with bash specifically, I'll start with
set -Eeuo pipefail
This covers more error handling in a similar fashion. I consider these as sane defaults for new bash programs. Refer to the bash manual for more information on what these options do.
To add to the accepted answer:
Bear in mind that set -e sometimes is not enough, specially if you have pipes.
For example, suppose you have this script
#!/bin/bash
set -e
./configure > configure.log
make
... which works as expected: an error in configure aborts the execution.
Tomorrow you make a seemingly trivial change:
#!/bin/bash
set -e
./configure | tee configure.log
make
... and now it does not work. This is explained here, and a workaround (Bash only) is provided:
#!/bin/bash
set -e
set -o pipefail
./configure | tee configure.log
make
The if statements in your example are unnecessary. Just do it like this:
dosomething1 || exit 1
If you take Ville Laurikari's advice and use set -e then for some commands you may need to use this:
dosomething || true
The || true will make the command pipeline have a true return value even if the command fails so the the -e option will not kill the script.
If you have cleanup you need to do on exit, you can also use 'trap' with the pseudo-signal ERR. This works the same way as trapping INT or any other signal; bash throws ERR if any command exits with a nonzero value:
# Create the trap with
# trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah
Or, especially if you're using "set -e", you could trap EXIT; your trap will then be executed when the script exits for any reason, including a normal end, interrupts, an exit caused by the -e option, etc.
The $? variable is rarely needed. The pseudo-idiom command; if [ $? -eq 0 ]; then X; fi should always be written as if command; then X; fi.
The cases where $? is required is when it needs to be checked against multiple values:
command
case $? in
(0) X;;
(1) Y;;
(2) Z;;
esac
or when $? needs to be reused or otherwise manipulated:
if command; then
echo "command successful" >&2
else
ret=$?
echo "command failed with exit code $ret" >&2
exit $ret
fi
Run it with -e or set -e at the top.
Also look at set -u.
On error, the below script will print a RED error message and exit.
Put this at the top of your bash script:
# BASH error handling:
# exit on command failure
set -e
# keep track of the last executed command
trap 'LAST_COMMAND=$CURRENT_COMMAND; CURRENT_COMMAND=$BASH_COMMAND' DEBUG
# on error: print the failed command
trap 'ERROR_CODE=$?; FAILED_COMMAND=$LAST_COMMAND; tput setaf 1; echo "ERROR: command \"$FAILED_COMMAND\" failed with exit code $ERROR_CODE"; put sgr0;' ERR INT TERM
An expression like
dosomething1 && dosomething2 && dosomething3
will stop processing when one of the commands returns with a non-zero value. For example, the following command will never print "done":
cat nosuchfile && echo "done"
echo $?
1
#!/bin/bash -e
should suffice.
I am just throwing in another one for reference since there was an additional question to Mark Edgars input and here is an additional example and touches on the topic overall:
[[ `cmd` ]] && echo success_else_silence
Which is the same as cmd || exit errcode as someone showed.
For example, I want to make sure a partition is unmounted if mounted:
[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1

Bash - execute commands in opened a program

Code looks like this:
#!/bin/bash
X=$(pgrep weechat)
re='^[0-9]+$'
if [[ $X =~ $re ]] ; then
echo "process '$X' killed"
`kill -9 $X`
else
echo "no running weechat sessions"
fi
weechat
sleep .1
echo "/connect secure"
The last "echo" needs to write "/connect secure" and hit enter inside of the weechat program
How do you recommend I do this?
According to WeeChat user’s guide there is a FIFO created that accepts commands "if option plugins.var.fifo.fifo is enabled, it is by default".
Simply grab the PID of the weechat process (in order to find the FIFO file):
weechat &
weechat_pid=$!
printf '%s\n' "/connect secure" >~/.weechat/weechat_fifo_${weechat_pid}
I'd also suggest a change to your kill command as listed; doing this:
`kill -9 $X`
attempts to execute a command that is the stdout resulting from the kill command. You're simply trying to kill the process, so just run the kill command:
kill -9 "$X"
or since you're already relying on pgrep, just use pkill:
pkill -9 "$X"
and since you just seem to be checking whether pgrep found a PID or not, just check it's return code:
pgrep weechat >/dev/null && pkill weechat

conditional redirection in bash

I have a bash script that I want to be quiet when run without attached tty (like from cron).
I now was looking for a way to conditionally redirect output to /dev/null in a single line.
This is an example of what I had in mind, but I will have many more commands that do output in the script
#!/bin/bash
# conditional-redirect.sh
if tty -s; then
REDIRECT=
else
REDIRECT=">& /dev/null"
fi
echo "is this visible?" $REDIRECT
Unfortunately, this does not work:
$ ./conditional-redirect.sh
is this visible?
$ echo "" | ./conditional-redirect.sh
is this visible? >& /dev/null
what I don't want to do is duplicate all commands in a with-redirection or with-no-redirection variant:
if tty -s; then
echo "is this visible?"
else
echo "is this visible?" >& /dev/null
fi
EDIT:
It would be great if the solution would provide me a way to output something in "quiet" mode, e.g. when something is really wrong, I might want to get a notice from cron.
For bash, you can use the line:
exec &>/dev/null
This will direct all stdout and stderr to /dev/null from that point on. It uses the non-argument version of exec.
Normally, something like exec xyzzy would replace the program in the current process with a new program but you can use this non-argument version to simply modify redirections while keeping the current program.
So, in your specific case, you could use something like:
tty -s
if [[ $? -eq 1 ]] ; then
exec &>/dev/null
fi
If you want the majority of output to be discarded but still want to output some stuff, you can create a new file handle to do that. Something like:
tty -s
if [[ $? -eq 1 ]] ; then
exec 3>&1 &>/dev/null
else
exec 3>&1
fi
echo Normal # won't see this.
echo Failure >&3 # will see this.
I found another solution, but I feel it is clumsy, compared to paxdiablo's answer:
if tty -s; then
REDIRECT=/dev/tty
else
REDIRECT=/dev/null
fi
echo "Normal output" &> $REDIRECT
You can use a function:
function the_code {
echo "is this visible?"
# as many code lines as you want
}
if tty -s; then # or other condition
the_code
else
the_code >& /dev/null
fi
This works well for me. If DUMP_FILE is empty things go to stdout otherwise to the file. It does the job without using explicit redirection, but just uses pipes and existing applications.
function stdout_or_file
{
local DUMP_FILE=${1:-}
if [ -z "${DUMP_FILE}" ]; then
cat
else
sed -n "w ${DUMP_FILE}"
fi
}
function foo()
{
local MSG=$1
echo "info: ${MSG}"
}
foo "bar" | stdout_or_file ${DUMP_FILE}
Of course, you can squeeze this also in one line
foo "bar" | if [ -z "${DUMP_FILE}" ]; then cat; else sed -n "w ${DUMP_FILE}"; fi
Besides sed -n "w ${DUMP_FILE}" another command that does the same is dd status=none of=${DUMP_FILE}
The simplest solution is to use eval (a shell builtin), as it will act on the redirection in the expanded variable... and also act on anything else in the command line, so add extra quoting as required (note the extra single quotes added around the echo string below due to the '?' which would otherwise cause shell filename expansion to be attempted).
#!/bin/bash
# conditional-redirect.sh
if tty -s; then
REDIRECT=
else
REDIRECT=">& /dev/null"
fi
eval echo '"is this visible?"' $REDIRECT

Resources