This question already has answers here:
Bash script prints "Command Not Found" on empty lines
(17 answers)
Closed 6 years ago.
I am trying to learn more shell scripting. The available shells on this machine are /bin/sh, /bin/csh, and /bin/tcsh with sh being default and used here. OS is FreeBSD 9.1-RELEASE.
My current project needs to check whether a process updated the database yesterday. The first two echoes are just there for the moment verifying the variables have what I think they do.
#!/bin/sh
lastcheck=$(mysql -h dbserver.mysite.com -u myuser -pmypass mydb -se "SELECT MAX(DATE_FORMAT(datetime_sent_to_fulfiller,'%Y%m%d')) FROM print_mailing_request;"|cut -f1)
yesterday=$(echo -e "$(TZ=GMT+30 date +%Y%m%d)\n$(TZ=GMT+20 date +%Y%m%d)" | grep -v $(date +%Y-%m-%d) | tail -1)
echo "previous day was $yesterday"
echo "we last checked on $lastcheck"
if [ "$lastcheck" -eq "$yesterday" ]; then
echo "cool"
else
echo "uncool"
fi;
One question is why the : not found: output is showing up and how do I prevent it?
Another question is why both 'cool' and 'uncool' are being echoed?
Last question is why 'else' is being echoed?
$ /bin/sh pmr.cron.sh
: not found:
previous day was 20160602
we last checked on 20160602
: not found:
: not found:
cool
: not found: else
uncool
: not found:
You have carriage returns in your script; that generates the "not found" messages and is probably why both branches of your if are getting generated.
Your dates are comparable as strings, no need to use -eq to compare them as numbers.
Related
This question already has answers here:
How do I set a variable to the output of a command in Bash?
(15 answers)
Command not found error in Bash variable assignment
(5 answers)
Closed 1 year ago.
I have problem with this script
The Output is always "Your string has been found" even if there isn't anything in /etc/hosts.
Do you have any ideas how to solve this problem or is it even possible to do this?
Any feedback is appreciated. I'm beginning to learn bash so feel free to critize me.
Thank you.
#!/bin/bash
google= dig +noall +answer www.google.com | awk '{ print $1, $NF }'
hosts= cat /etc/hosts | grep "www.google.com"
if [ $google == $hosts ] ; then
echo "Your string has been found"
else
echo "Your string has not been found"
fi
This question already has answers here:
Bash syntax error: unexpected end of file
(21 answers)
Closed 8 years ago.
#!/bin/bash
maxDate='00000000'
fileDate='20140507'
if [[ $maxDate == '00000000' ]]; then
echo "right"
fi
echo $fileDate
This make me really crazy, I have spend whole day to deal with this format stuff.
the script is like above it print out unexpected end of file
if I delete the last line, it will not print anything which is not correct.
I really don't know what wrong with it.
Remove the DOS line endings from your script with dos2unix. If that is not available, the following can be used:
tr -d '\r' < myscript > myscript.tmp
mv myscript.tmp myscript
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
I have a date in file called lastbackup with date in the yyyymmdd format. Now, I would like to compare this string with current date in the same format in BASH.
I have:
#!/bin/bash
cd ~
DATE=`date +%Y%m%d`
LASTBACKUP=sudo cat ./lastbackup
echo $LASTBACKUP
echo $DATE
if [ "$LASTBACKUP" == "$DATE" ]; then
echo "Do nothing, already processed."
else
echo "Do something, not processed"
echo -n $DATE > ./lastbackup
fi
I keep getting Do something... and I can't figure out why DATE and LASTBACKUP variables are different, because in output it looks like they are equal:
$ testeq
20140407
20140407
Do something, not processed
When I use (as suggested by Alex):
echo "LASTBACKUP: '$LASTBACKUP'"
echo "DATE: '$DATE'"
Then I get:
$ testeq
20140407LASTBACKUP: ''
DATE: '20140407'
And now it is clear to me what is going on.
Here's the reason this is wrong:
LASTBACKUP=sudo cat ./lastbackup
When you execute a command in this form:
var=val var2=val2 ... command arg arg ...
The variables are put into the environment of the command, and only into that environment. So, while cat is running, it has an environment variable named LASTBACKUP with the value "sudo". Once cat is finished, that variable will disappear.
The contents of the ./lastbackup file get cat'ed to stdout. I'm surprised you don't see a blank line when you echo $LASTBACKUP. Check your files for CRLF line endings, or perhaps the ./lastbackup file does not end with a newline.
When you're debugging and printing variables out, do something more obvious to see their value:
printf "LASTBACKUP='%s'\n" "$LASTBACKUP"
# or
for var in DATE LASTBACKUP; do
printf "%s='%s'\n" "$var" "${!var}"
done
bash has a builtin way to read a file so you don't have to call cat: this will have you a couple of milliseconds:
lastbackup=$(< ./lastbackup)
Get out of the habit of using ALL_CAPS_VARS -- one day you'll use PATH=something and then wonder why your script stops working.
Yep, the problem is probably that you're not actually initializing the LASTBACKUP variable anywhere.
But the fact that you're trying to read it with sudo bothers me. Doesn't the user that runs the script has permission to read a file that is in its $HOME? You are cd'ing into ~, which is the user's home, but since you're sudoing its read makes me think that maybe that file is in /root/lastbackup? Why would you sudo its reading otherwise?
This should do the trick:
#!/bin/bash
# For testings:
# date +%Y%m%d > $HOME/lastbackup # Uncomment to create a "./lastbackup" test file.
# Let's beging
cd ~
DATE=`date +%Y%m%d`
LASTBACKUP=`cat ./lastbackup`
echo $LASTBACKUP
echo $DATE
if [ "$LASTBACKUP" == "$DATE" ]; then
echo "Do nothing, already processed."
else
echo "Do something, not processed"
echo -n $DATE > ./lastbackup
fi
Another option, if you're running a fairly new version of bash, and since your date format doesn't have any non-number character as separator (no - , / ...) would be consider the values numbers in the if:
if (( $LASTBACKUP == $DATE )); then
echo "Do nothing, already processed."
else
echo "Do something, not processed"
echo -n $DATE > ./lastbackup
fi
And, while testing, don't escape the LASTBACKUP or DATE variables with quotation marks ". If there was a problem reading the file (or getting the current date, but I doubt that one) you'll see an error. It'll help debugging.
This question already has answers here:
grep for expression containing variable
(2 answers)
Closed 9 years ago.
In my bash script, I am attempting to parse through a status file and detect errors based on some keywords. I store these prefixes in a list, and then loop through them.
Bash script:
status_page="/path/to/file.txt"
list="aaa bbb ccc ddd"
for pre in $list
do
echo "grep '\w\w\w${pre}-.*\.lin failed' ${status_page}" # debug
if grep '\w\w\w${pre}-.*\.lin failed' ${status_page}; then
echo "Found error!"
exit 8;
fi
done
/path/to/file.txt:
xyzfff-tool.lin failed
xyzggg-exec.lin failed
rstccc-tool.lin failed
The bash script should catch the line rstccc-tool.lin failed, but it skips right over it.
For debugging, I print the grep commands verbatim, and when I copy that line and issue the command in my shell (tcsh), it returns that line...
Shell:
$ grep '\w\w\wccc-.*\.lin failed' /path/to/file.txt
rstccc-tool.lin failed
$ echo $?
0
If grep can find the line when I issue the command normally, how come it won't find it when the bash script is calling grep?
The variable won't be expanded in single quotes. Try with double quotes:
if grep "\w\w\w${pre}-.*\.lin failed" "${status_page}"; then
The ${pre} portion of that script is not parsing it correctly. I believe you need that line to say:
if grep '\w\w\w'${pre}'-.*\.lin failed' ${status_page}; then
... where the ${pre} is outside the quotation, such that bash will do the correct string replacement before sending it to grep.
I am trying to echo the last command run inside a bash script. I found a way to do it with some history,tail,head,sed which works fine when commands represent a specific line in my script from a parser standpoint. However under some circumstances I don't get the expected output, for instance when the command is inserted inside a case statement:
The script:
#!/bin/bash
set -o history
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"
case "1" in
"1")
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"
;;
esac
The output:
Tue May 24 12:36:04 CEST 2011
last command is [date]
Tue May 24 12:36:04 CEST 2011
last command is [echo "last command is [$last]"]
[Q] Can someone help me find a way to echo the last run command regardless of how/where this command is called within the bash script?
My answer
Despite the much appreciated contributions from my fellow SO'ers, I opted for writing a run function - which runs all its parameters as a single command and display the command and its error code when it fails - with the following benefits:
-I only need to prepend the commands I want to check with run which keeps them on one line and doesn't affect the conciseness of my script
-Whenever the script fails on one of these commands, the last output line of my script is a message that clearly displays which command fails along with its exit code, which makes debugging easier
Example script:
#!/bin/bash
die() { echo >&2 -e "\nERROR: $#\n"; exit 1; }
run() { "$#"; code=$?; [ $code -ne 0 ] && die "command [$*] failed with error code $code"; }
case "1" in
"1")
run ls /opt
run ls /wrong-dir
;;
esac
The output:
$ ./test.sh
apacheds google iptables
ls: cannot access /wrong-dir: No such file or directory
ERROR: command [ls /wrong-dir] failed with error code 2
I tested various commands with multiple arguments, bash variables as arguments, quoted arguments... and the run function didn't break them. The only issue I found so far is to run an echo which breaks but I do not plan to check my echos anyway.
Bash has built in features to access the last command executed. But that's the last whole command (e.g. the whole case command), not individual simple commands like you originally requested.
!:0 = the name of command executed.
!:1 = the first parameter of the previous command
!:4 = the fourth parameter of the previous command
!:* = all of the parameters of the previous command
!^ = the first parameter of the previous command (same as !:1)
!$ = the final parameter of the previous command
!:-3 = all parameters in range 0-3 (inclusive)
!:2-5 = all parameters in range 2-5 (inclusive)
!! = the previous command line
etc.
So, the simplest answer to the question is, in fact:
echo !!
...alternatively:
echo "Last command run was ["!:0"] with arguments ["!:*"]"
Try it yourself!
echo this is a test
echo !!
In a script, history expansion is turned off by default, you need to enable it with
set -o history -o histexpand
The command history is an interactive feature. Only complete commands are entered in the history. For example, the case construct is entered as a whole, when the shell has finished parsing it. Neither looking up the history with the history built-in (nor printing it through shell expansion (!:p)) does what you seem to want, which is to print invocations of simple commands.
The DEBUG trap lets you execute a command right before any simple command execution. A string version of the command to execute (with words separated by spaces) is available in the BASH_COMMAND variable.
trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"
Note that previous_command will change every time you run a command, so save it to a variable in order to use it. If you want to know the previous command's return status as well, save both in a single command.
cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi
Furthermore, if you only want to abort on a failed commands, use set -e to make your script exit on the first failed command. You can display the last command from the EXIT trap.
set -e
trap 'echo "exit $? due to $previous_command"' EXIT
Note that if you're trying to trace your script to see what it's doing, forget all this and use set -x.
After reading the answer from Gilles, I decided to see if the $BASH_COMMAND var was also available (and the desired value) in an EXIT trap - and it is!
So, the following bash script works as expected:
#!/bin/bash
exit_trap () {
local lc="$BASH_COMMAND" rc=$?
echo "Command [$lc] exited with code [$rc]"
}
trap exit_trap EXIT
set -e
echo "foo"
false 12345
echo "bar"
The output is
foo
Command [false 12345] exited with code [1]
bar is never printed because set -e causes bash to exit the script when a command fails and the false command always fails (by definition). The 12345 passed to false is just there to show that the arguments to the failed command are captured as well (the false command ignores any arguments passed to it)
I was able to achieve this by using set -x in the main script (which makes the script print out every command that is executed) and writing a wrapper script which just shows the last line of output generated by set -x.
This is the main script:
#!/bin/bash
set -x
echo some command here
echo last command
And this is the wrapper script:
#!/bin/sh
./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'
Running the wrapper script produces this as output:
echo last command
history | tail -2 | head -1 | cut -c8-
tail -2 returns the last two command lines from history
head -1 returns just first line
cut -c8- returns just command line, removing PID and spaces.
There is a racecondition between the last command ($_) and last error ( $?) variables. If you try to store one of them in an own variable, both encountered new values already because of the set command. Actually, last command hasn't got any value at all in this case.
Here is what i did to store (nearly) both informations in own variables, so my bash script can determine if there was any error AND setting the title with the last run command:
# This construct is needed, because of a racecondition when trying to obtain
# both of last command and error. With this the information of last error is
# implied by the corresponding case while command is retrieved.
if [[ "${?}" == 0 && "${_}" != "" ]] ; then
# Last command MUST be retrieved first.
LASTCOMMAND="${_}" ;
RETURNSTATUS='✓' ;
elif [[ "${?}" == 0 && "${_}" == "" ]] ; then
LASTCOMMAND='unknown' ;
RETURNSTATUS='✓' ;
elif [[ "${?}" != 0 && "${_}" != "" ]] ; then
# Last command MUST be retrieved first.
LASTCOMMAND="${_}" ;
RETURNSTATUS='✗' ;
# Fixme: "$?" not changing state until command executed.
elif [[ "${?}" != 0 && "${_}" == "" ]] ; then
LASTCOMMAND='unknown' ;
RETURNSTATUS='✗' ;
# Fixme: "$?" not changing state until command executed.
fi
This script will retain the information, if an error occured and will obtain the last run command. Because of the racecondition i can not store the actual value. Besides, most commands actually don't even care for error noumbers, they just return something different from '0'. You'll notice that, if you use the errono extention of bash.
It should be possible with something like a "intern" script for bash, like in bash extention, but i'm not familiar with something like that and it wouldn't be compatible as well.
CORRECTION
I didn't think, that it was possible to retrieve both variables at the same time. Although i like the style of the code, i assumed it would be interpreted as two commands. This was wrong, so my answer devides down to:
# Because of a racecondition, both MUST be retrieved at the same time.
declare RETURNSTATUS="${?}" LASTCOMMAND="${_}" ;
if [[ "${RETURNSTATUS}" == 0 ]] ; then
declare RETURNSYMBOL='✓' ;
else
declare RETURNSYMBOL='✗' ;
fi
Although my post might not get any positive rating, i solved my problem myself, finally.
And this seems appropriate regarding the intial post. :)