Shell Script execution of Perl code causing weird results - perl

I have a cron job that runs several shell scripts:
30 1 * * 1-5 /ufs/00/home/usr/bin/ConsentforFoo.sh "prd"
15 1 * * 1-5 /ufs/00/home/usr/bin/apptTvoxforFoo.sh
the first shell script looks like this:
#!/bin/bash
# ConsentforFoo.sh - set different environments, set path to perl scripts, calls script
TMP_HOME=/home/localweb/htdocs/cgi-bin/usr/CFoodir
if [ "$1" = "dev" ] || [ "$1" = "uat" ] || [ "$1" = "prd" ]
then
cd $TMP_HOME/$1
My-Consent-Cron.pl
else
echo "Val Not Set: $1"
fi
this script works flawlessly... However, the second shell script looks like this:
#!/bin/bash
# apptTvoxforFoo.sh - sends MHT population and patients with multiple appointments to west
TMP_HOME=/home/localweb/htdocs/cgi-bin/usr/CFoodir
cd $TMP_HOME
TvoxCron.pl #adding './' works here
but when it runs, I get an error saying: "sh: /ufs/00/home/usr/bin/apptTvoxforFoo.sh: cannot execute"
I add a "pwd" to the shell script and it's getting in the right directory and the file is there...
Weirdest thing is when I add "./" to it, it works... but in the first shell script I don't have to...
Any ideas why taking the if/then/else out would force me to SOURCE the perl script?
Thanks for any help you can provide.

Did you check the file permissions on the directories and all the files? Can you add a dot in front of the file to make sure you are not finding the file on the path?
./TvoxCron.pl

Related

sh script gets stuck on read command in while loop

I'm trying to write a script that I'll put in my pi's cron to check for network connectivity every 10 seconds, if it fails a ping to google it will write a text file as false, then next time it succeeds, it will restart a program, because the specific program has issues with reconnecting to the network automatically.
The script seemed to be working when I was executing it from the terminal out of the same directory, then I cd back to / and added a bunch of comments, and now it just exits the script without any output, and for the life of me I can't figure out where i messed it up - I'm still relatively new to scripting so I could be missing something absolutely obvious here, but I couldn't find anything useful on google.
file heirarchy:
/home/pi/WEB_UI/
inside the WEB_UI folder are both of the scripts i'm running here.
nonet.sh - the script in question
pianobar.sh - a simple script to pkill a program and reload it after 5 seconds.
var.txt - a text file that will only ever contain "true" or "false
I've tried removing all of the comments, changing the file locations to ./ and making the while; do commands a single line, but I can't figure out where the issue is. if I run sh -x for the script, it returns:
pi#raspberrypi:~/WEB_UI $ sh -x nonet.sh
+ ping -q -c 1 -W 1 google.com
+ read line
interestingly I get the same result from a test script I was using that was basically
"if var.txt says 'true', echo 'up', else echo 'down'"
I wonder if something is wrong with my sh interpreter?
#!/bin/sh
#ping google, if successful return true
if ping -q -c 1 -W 1 google.com >/dev/null; then
#read variable line, perform action do
while read line
do
#leading $ means return previous output. if line is false:
if [ "$line" = "false" ]
then
#return network up text, run pianobar script, set var.txt to true.
echo "the network is back up"
sh /home/pi/WEB_UI/pianobar.sh
echo true > /home/pi/WEB_UI/var.txt
else
#otherwise return network is up, set var.txt to true
echo "the network is up"
echo true > /home/pi/WEB_UI/var.txt
#fi ends an if statement, done ends a while loop.
#text after done tells the while loop where to get the line variable
fi
done < /home/pi/WEB_UI/var.txt
else
while read line
do
if [ "$line" = "false" ]
then
#if var.txt is already false, ping google again
if ping -q -c 1 -W 1 google.com >/dev/null; then
#if ping works, the network is back, restart pianobar, set var to true
echo "the network is back up"
sh /home/pi/WEB_UI/pianobar.sh
echo true > /home/pi/WEB_UI/var.txt
else
#if var.txt is false, network is still down. wait.
echo "the network is still down"
fi
else
echo "the network is down"
echo false > /home/pi/WEB_UI/var.txt
fi
done < /home/pi/WEB_UI/var.txt
fi
the script SHOULD just echo a simple line saying whether the network is up, down, back up, or still down, depending on how many flags it passes/fails. Any assistance would be greatly appreciated!
as Shellter said in comments above, the issue was that I needed to add \n to the end of the line in my var.txt
I think I saw another post recently where while read... was frustrated by a missing \n char, so maybe you want to do printf "false\n" > file instead. Good luck.

How can I make a function run every time cd successfully changes to another directory within sh on FreeBSD?

I'm using sh as my shell on FreeBSD but I want to be able to have a pretty prompt like the one bash gives me on Ubuntu. There are two things that the FreeBSD implementation of sh seems to lack as far as PS1 escape characters go:
The \w works but does not expand $HOME to ~, so this is something I have already hacked up myself
I can use PS1 to update the prompt on the terminal, but as far as I can tell it is not possible to use the PS1 variable to update the title bar as well. ESC and BEL fail to set the title as one would expect if they were using bash or ksh
Here is my .shrc file
update_prompt() {
case "$PWD" in
"$HOME"*)
pretty_pwd="~${PWD#*"${HOME}"}"
;;
"/usr$HOME"*)
pretty_pwd="~${PWD#*"/usr${HOME}"}"
;;
*)
pretty_pwd="$PWD"
;;
esac
case "$TERM" in
xterm*|rxvt*)
PS1="[$USER#\\h $pretty_pwd]\\$ "
;;
*)
;;
esac
printf "\\033]0;[%s#$(hostname -s): %s]\\007" "$USER" "$pretty_pwd"
}
update_prompt
So when I fire up a terminal or log in via ssh, it gives the pretty prompt that I like. But now I need this function to run every time that cd is executed and returns an exit status of 0.
I was going to use an alias that was something like:
alias cd="cd $1 && update_prompt"
but that was before I realized that aliases do not except arguments. How might I go about doing something like this?
You can use a function instead of an alias:
cd() {
command cd "$#" && update_prompt
}
Just put it into ~/.shrc. You have to use command here to let sh know that you are referring to the actual cd builtin command instead of the function you've just defined.
Refer to the sh(1) manual page for the details on how to make sh(1) source the ~/.shrc file when it starts:
Therefore, a user should place commands that are to be executed only at login
time in the .profile file, and commands that are executed for every shell
inside the ENV file. The user can set the ENV variable to some file by placing
the following line in the file .profile in the home directory, substituting for
.shrc the filename desired:
ENV=$HOME/.shrc; export ENV
I use this trick in my cd alias manager. Here's a link to the source code of the function: https://github.com/0mp/goat/blob/v2.5.0/libgoat.sh#L31-L57
You can do it with alias+arguments if you swap the commands:
$ alias cd="echo change; cd"
$ pwd
/nas
$ cd /
change
$ pwd
/
$ cd /etc
change
$ pwd
/etc
$

How can I store output string to a variable AND display in console

I have a perl script that prints a message. This script is being called by GNU make. In my GNU make, I want to display the message printed out by the script AND store it in a variable also.
I'm doing it this way.
result=`$(PERL) parse.pl report.log` #parse the report
echo $(result) #echo the message here
ifneq ($(strip $$(result)),) #check if message is empty
#if not empty, search for filepath string pattern and exit
echo filepath
exit 1
endif
But it is not displaying the string message from parse.pl.
You are capturing into a shell variable, but then trying to echo a makefile variable (and even if you tried to echo the shell variable, that wouldn't work because make runs each line in a separate shell process).
Changing it to echo the shell varible and all to run in one shell should work:
foo:
result=`$(PERL) parse.pl report.log`; \
echo $$result
but whatever you later need to do to use the captured result would also need to be in the same shell execution.
Apparently you can capture into a makefile variable too, which may be more convenient:
foo:
$(eval result := $(shell $(PERL) parse.pl report.log))
echo $(result)
The critical thing to keep in mind with make is first, that the entire makefile is parsed before any rules are run, and second a makefile has two completely distinct syntaxes in it: makefile syntax for most of it, and shell syntax for the recipes. The shell syntax is run by the shell, not by make: make just starts a shell, hands over the recipe, and waits for the shell to exit to see if it worked or not.
As a result of this you CANNOT combine make constructs like ifeq with shell commands and their results: it cannot work because all the make constructs are parsed first, while the makefile is being read in, and the shell commands are not run until much later, when the target is to be built.
In your case you need to write the entire thing in shell syntax, because you want things to depend on the shell invocation.
So, like this:
foo:
result=`$(PERL) parse.pl report.log`; \
echo $$result; \
if [ "$$result" = "" ]; then \
echo filepath; \
exit 1; \
fi
Note how each line ends with a backslash, so it's appended to the previous line instead of being a separate line: make runs each separate line in a different shell.
Alternatively if you have a new-enough GNU make you can use the one shell feature:
.ONESHELL:
foo:
result=`$(PERL) parse.pl report.log`
echo $$result
if [ "$$result" = "" ]; then
echo filepath
exit 1
fi

Including some SFTP commands in a Makefile

I use a Makefile to create pdfs of papers I'm working on. I'd also like to use make to upload the latest version to my website, which requires sftp. I though I could do something like this (which words on the command line) but it seems that in make, the EOF is getting ignored i.e., this
website:
sftp -oPort=2222 me#mywebsite.com << EOF
cd papers
put research_paper.pdf
EOF
generates an error message
cd papers
/bin/sh: line 0: cd: papers: No such file or directory
which I think is saying "papers" doesn't exist on your local machine i.e., the 'cd' is being executed locally, not remotely.
Couple of ideas:
use ncftp which every Linux distro as well as brew should have: it remembers 'state' so the cd becomes unnecessary
use scp instead of sftp if possible
write a trivial shell script doing the EOF business and call that
For what it is worth, here is my script to push tarballs to the CRAN winbuilder -- and takes target directory and script as arguments to ncftpput.
#!/bin/bash
function errorexit () {
echo "Error: $1"
exit 1
}
if [ "$#" -lt 1 ]; then
errorexit "Need to specify argument file"
fi
if [ ! -f ${1} ]; then
errorexit "File ${1} not found, aborting."
fi
ncftpput win-builder.r-project.org /R-release ${1}
ncftpput win-builder.r-project.org /R-devel ${1}
I then just do wbput.sh foo_1.2-3.tar.gz and off it goes...
You cannot (normally) put a single command on multiple lines in a Make recipe, so here documents are a no-go. Try this instead:
website: research_paper.pdf
printf 'cd papers\nput $<\n' \
| sftp -oPort=2222 me#mywebsite.com
The target obviously depends on the PDF, so I made it an explicit dependency, as well.

Script response if md5sum returns FAILED

Say I had a script that checked honeypot locations using md5sum.
#!/bin/bash
#cryptocheck.sh
#Designed to check md5 CRC's of honeypot files located throughout the filesystem.
#Must develop file with specific hashes and create crypto.chk using following command:
#/opt/bin/md5sum * > crypto.chk
#After creating file, copy honeypot folder out to specific folders
locations=("/share/ConfData" "/share/ConfData/Archive" "/share/ConfData/Application"
"/share/ConfData/Graphics")
for i in "${locations[#]}"
do
cd "$i/aaaCryptoAudit"
/opt/bin/md5sum -c /share/homes/admin/crypto.chk
done
And the output looked like this:
http://pastebin.com/b4AU4s6k
Where would you start to try and recognize the output and perhaps trigger some sort of response by the system if there is a 'FAILED'?
I've worked a bit with PERL trying to parse log files before but my attempts typically failed miserably for one reason or another.
This may not be the proper way to go about this, but I'd want to be putting this script into a cronjob that would run every minute. I had some guys telling me that an inotify job or script (I'm not familiar with this) would be better than doing it this way.
Any suggestions?
--- edit
I made another script to call the script above and send the output to a file. The new script then runs a grep -q on 'FAILED' and if it picks anything up, it sounds the alarm (tbd what the alarm will be).
#!/bin/bash
#cryptocheckinit.sh
#
#rm /share/homes/admin/cryptoalert.warn
/share/homes/admin/cryptocheck.sh > /share/homes/admin/cryptoalert.warn
grep -q "FAILED" /share/homes/admin/cryptoalert.warn && echo "LIGHT THE SIGNAL FIRES"
Use:
if ! /opt/bin/md5sum -c /share/homes/admin/crypto.chk
then
# Do something
fi
Or pipe the output of the loop:
for i in "${locations[#]}"
do
cd "$i/aaaCryptoAudit"
/opt/bin/md5sum -c /share/homes/admin/crypto.chk
done | grep -q FAILED && echo "LIGHT THE SIGNAL FIRES"