Run bash script with defaults to piped commands set within the script - linux

Two questions about the same thing I think...
Question one:
Is it possible to have a bash script run with default parameters/options? ...in the sense if someone were to run the script:
./somescript.sh
it would actually run with ./somescript.sh | tee /tmp/build.txt?
Question two:
Would it also possible to prepend the script with defaults? For example, if you were to run the script ./somescript.sh
it would actually run
script -q -c "./somescript.sh" /tmp/build.txt | aha > /tmp/build.html?
Any help or guidance is very much appreciated.

You need a wrapper script that handles all such scenario for you.
For example, your wrapper script can take parameters that helps you decide.
./wrapper_script.sh --input /tmp/build.txt --ouput /tmp/build.html
By default, --input and --output can be set to values you want when they are empty.

You can use the builtin $# to know how many arguments you have and take action based on that. If you want to do your second part, for example, you could do something like
if [[ $# -eq 0 ]]; then
script -q -c "$0 /tmp/build.txt | aha /tmp/build.html
exit
fi
# do everything if you have at least one argument
Though this will have problems if your script/path have spaces, so you're probably better putting the real path to your script in the script command instead of $0
You can also use exec instead of running the command and exiting, but make sure you have your quotes in the right place:
if [[ $# -eq 0 ]]; then
exec script -q -c "$0 /tmp/build.txt | aha /tmp/build.html"
fi
# do everything when you have at least 1 argument

Related

have arbitrary executable inherit errexit, if script is bash

I have a folder of executable scripts, and some of them have Python shebangs, while others have Bash shebangs, etc. We have a cron job that runs this folder of scripts nightly, and the hope is that any error in any script will exit the job.
The scripts are run with something like: for FILE in $FILES; do ./$FILE; done
The scripts are provided by various people, and while the Python scripts always exit after an error, sometimes developers forget to add set -e in their Bash scripts.
I could have the for-loop use bash -e, but then I need to detect whether the current script is Bash/Python/etc.
I could set -e from the parent script, and then source scripts, but I still need to know which language each script is in, and I'd prefer them to run as subshells so script contributors don't have to worry about messing up the parent.
greping the shebangs is a short tweak, but knowing the flexibility of Bash, I'd be surprised if there weren't a way to "export" an option that affected all child scripts, in the same way you can export a variable. And, there have been many cases in general where I've forgotten "set -e", so it could be nice to know more options for fool-proofing things.
I see some options for inheriting -e for subshells involved in command substitution, but not in general.
Disclaimer: Never, ever do this! It's a huge disservice to everyone involved. You will introduce failures both in scripts with meticulous error handling, and in scripts without it.
Anyways, no one likes being told "don't do that" on StackOverflow, so my suggestion would be to identify scripts and invoke them with their shebang string plus -e:
for f in ./*
do
# Determine if the script is a shell script
if [[ $(file -i "$f") == *text/x-shellscript* ]]
then
# Read the first line
read -r shebang < "$f"
# The script shouldn't have been identified as a shell script without
# a shebang, but check anyways
if [[ $shebang != "#!"* ]]
then
echo "No idea what $f is" >&2
continue
fi
# Strip off the #! and run it with -e and the file
shebang=${shebang#??}
$shebang -e "$f"
else
# It's some other kind of executable, just run it directly
"$f"
fi
done
Here's a script with correct error handling that now stops working:
#!/bin/bash
my-service start
ret=$?
if [ $ret -eq 127 ]
then
# Use legacy invocation instead
start-my-service
ret=$?
fi
exit "$ret"
Here's a script without error handling that now stops working:
#!/bin/sh
err=$(grep "ERROR" file.log)
if [ -z "$err" ]
then
echo "Run was successful"
exit 0
else
echo "Run failed: $err"
exit 1
fi

Use bash in conky.config

You can use bash code, and call bash scripts, in conky.text. Is there any way to use it in conky.config?
The reason I want this is to have window specifications depending on whether I have an external monitor connected or not.
So I want logic similar to this:
if xrandr -q | grep -oP 'HDMI2\sconnected' > /dev/null ; then
x=-900
else
x=0
fi
gap_x=$x
I personally do not encourage the following solution, but if all else fails, this will at least work very well.
Make a copy of your .conkyrc file, let's call it .conkyrc_dual, and make the bash file below:
#!/bin/bash
pkill conky
if xrandr -q | grep -oP 'HDMI2\sconnected' > /dev/null ; then
conky -c ~/.conkyrc_dual
notify-send 'Conky' 'Dual monitors'
else
conky
notify-send 'Conky' 'Single monitor'
fi
Now run this file when you want to start conky.
You could also have a bash script use sed to edit the gap_x variable in your .conkyrc file as needed before starting conky. That way, you'd only need a single config file. Keep a backup of .conkyrc, of course, just in case something goes terribly awry.

How to read the first line user types into terminal in bash script

I'm trying to write a script where to run the script, the user will type something along the lines of
$./cpc -c test1.txt backup
into the terminal, where ./cpc is to run the script, -c is $option, test1.txt is $source and backup is $destination.
How would I assign the values typed in to the terminal to use them in my script, for example in
if [[ -z $option || -z $source || -z $destination ]]; then
echo "Error: Incorrect number of arguments." (etc)
as when checking the script online the following errors return: 'option/source/destination is referenced but not assigned.'
Sorry in advance if any of this doesn't make sense, I'm trying to be as clear as possible
The arguments are stored in the numbered parameters $1, $2, etc. So, just assign them
option=$1
source=$2
destination=$3
See also man getopt or getopts in man bash.

Linux: start a script after another has finished

I read the answer for this issue from this link
in Stackoverflow.com. But I am so new in writing shell script that I did something wrong. The following are my scripts:
testscript:
#!/bin/csh -f
pid=$(ps -opid= -C csh testscript1)
while [ -d /proc/$pid ] ; do
sleep 1
done && csh testscript2
exit
testscript1:
#!/bin/csh -f
/usr/bin/firefox
exit
testscript2:
#!/bin/csh -f
echo Done
exit
The purpose is for testscript to call testscript1 first; once testscript1 already finish (which means the firefox called in script1 is closed) testscript will call testscript2. However I got this result after running testscript:
$ csh testscript
Illegal variable name.
Please help me with this issue. Thanks ahead.
I believe this line is not CSH:
pid=$(ps -opid= -C csh testscript1)
In general in csh you define variables like this:
set pid=...
I am not sure what the $() syntax is, perhaps back ticks woudl work as a replacement:
set pid=`ps -opid= -C csh testscript1`
Perhaps you didn't notice that the scripts you found were written for bash, not csh, but
you're trying to process them with the csh interpreter.
It looks like you've misunderstood what the original code was trying to do -- it was
intended to monitor an already-existing process, by looking up its process id using the process name.
You seem to be trying to start the first process from inside the ps command. But
in that case, there's no need for you to do anything so complicated -- all you need
is:
#!/bin/csh
csh testscript1
csh testscript2
Unless you go out of your way to run one of the scripts in the background,
the second script will not run until the first script is finished.
Although this has nothing to do with your problem, csh is more oriented toward
interactive use; for script writing, it's considered a poor choice, so you might be
better off learning bash instead.
Try,
below script will check testscript1's pid, if it is not found then it will execute testscirpt2
sp=$(ps -ef | grep testscript1 | grep -v grep | awk '{print $2}')
/bin/ls -l /proc/ | grep $sp > /dev/null 2>&1 && sleep 0 || /bin/csh testscript2

Why are commands executed in backquotes giving me different results when done in as script?

I have a script that I mean to be run from cron that ensures that a daemon that I wrote is working. The contents of the script file are similar to the following:
daemon_pid=`ps -A | grep -c fsdaemon`
echo "daemon_pid: " $daemon_pid
if [ $daemon_pid -eq 0 ]; then
echo "restarting fsdaemon"
/etc/init.d/fsdaemon start
fi
When I execute this script from the command prompt, the line that echoes the value of $daemon_pid is reporting a value of 2. This value is two regardless of whether my daemon is running or not. If, however, I execute the command with back quotes and then examine the $daemon_pid variable, the value of $daemon_pid is now one. I have also tried single stepping through the script using bashdb and, when I examine the variables using that tool, they are what they should be.
My question therefore is: why is there a difference in the behaviour between when the script is executed by the shell versus when the commands in the script are executed manually? I'm sure that there is something very fundamental that I am missing.
You're very likely encountering the grep as part of the 'answer' from ps.
To help fully understand what is happening, turn off the -c option, to see what data is being returned from just ps -A | grep fsdameon.
To solve the issue, some systems have a p(rocess)grep (pgrep). That will work, OR
ps -A | grep -v grep | grep -c fsdaemon
Is a common idiom you will see, but at the expense of another process.
The cleanest solution is,
ps -A | grep -c '[f]sdaemon'
The regular expression syntax should work with all greps, on all systems.
I hope this helps.
The problem is that grep itself shows up... Try running this command with anything after grep -c:
eple:~ erik$ ps -a | grep -c asdfladsf
1
eple:~ erik$ ps -a | grep -c gooblygoolbygookeydookey
1
eple:~ erik$
What does ps -a | grep fsdaemon return? Just look at the processes actually listed... :)
Since this is Linux, why not try the pgrep? This saves you a pipe, and you don't end up with grep reporting back the daemon script itself running.
Aany process with arguments including that name will add to the count - grep, and your script.
psing for a process isn't really reliable, you should use a lock file.
As several people have pointed out already, your process count is inflated because ps | grep detects (1) the script itself and (2) the subprocess created by the backquotes, which inherits the name of the main script. So an easy solution is to change the name of the script to something that doesn't include the name you're looking for. But you can do better.
The "best-practice" solution that I would suggest is to use the facilities provided by your operating system. It's not uncommon for an init script to create a PID file as part of the process of starting your daemon; in other words, instead of just running the daemon itself, you use a wrapper script that starts the daemon and then writes the process ID to a file somewhere. If start-stop-daemon exists on your system (and I think it's fairly common these days), you can use that like so:
start-stop-daemon --start --quiet --background \
--make-pidfile --pidfile /var/run/fsdaemon.pid -- /usr/bin/fsdaemon
(obviously replace the path /usr/bin/fsdaemon as appropriate) to start it, and then
start-stop-daemon --stop --quiet --pidfile /var/run/fsdaemon.pid
to stop it. start-stop-daemon has other options that might be useful to you, which you can investigate by reading the man page.
If you don't have access to start-stop-daemon, you can write a wrapper script to do basically the same thing, something like this to start:
echo "$$" > /var/run/fsdaemon.pid
exec /usr/bin/fsdaemon
and this to stop:
kill $(< /var/run/fsdaemon/pid)
rm /var/run/fsdaemon.pid
(this is pretty crude, of course, but it should normally work).
Anyway, once you have the setup to generate a PID file, whether by using start-stop-daemon or not, you can update your check script to this:
daemon_pid=`ps --no-headers --pid $(< /var/run/fsdaemon.pid) | wc -l`
if [ $daemon_pid -eq 0 ]; then
echo "restarting fsdaemon"
/etc/init.d/fsdaemon restart
fi
(one would think there would be a concise command to check whether a given PID is running, but I don't know it).
If you don't want to (or can't) create a PID file, I would at least suggest pgrep instead of ps | grep, since pgrep will search directly for a process by name and won't find anything that just happens to include the same string.
daemon_pid=`pgrep -x -c fsdaemon`
if [ $daemon_pid -eq 0 ]; then
echo "restarting fsdaemon"
/etc/init.d/fsdaemon restart
fi
The -x means "match exactly", and -c works as with grep.
By the way, it seems a bit misleading to name your variable daemon_pid when it is actually a count.

Resources