I'm making a script that document the system resources usage every certain interval.
But for some reason several commands give me this error:
myscript: /path/to/my/FILE is not a directory
These lines give me this error:
Input:
echo -e "Date: $(date +%H:%M:%S) /Hs/Mins/Segs" "\n" >> ~/Desktop/ShutDownTest/"$ExecutionNumber"/Ram/'Ram_Attempt_'"$RamAttempt".txt
echo -e 'The 3 most RAM usage processes:' "\n" "$(top -b -o %MEM -n 1 | head -n 10 | tail -n 4)" "\n" 2>> ~/Desktop/ShutDownTest/"$Execution_Number"/Errors.txt >> ~/Desktop/ShutDownTest/"$ExecutionNumber"/Ram/'Ram_Attempt_'"$RamAttempt".txt
echo -e "RAM usage:" "\n" 2>> ~/Desktop/ShutDownTest/"$ExecutionNumber"/Errors.txt >> ~/Desktop/ShutDownTest/"$ExecutionNumber"/Ram/'Ram_Attempt_'"$RamAttempt".txt
PD: $ExecutionNumber = 1 ; $RamAttempt is also equal to 1
I don't know why is this happening i executed all the 3 commands and redirect it to a file and all was all right.
Idk why i got this error or even what means, i mean i'm redirecting output to a file, of course that a file is not a directory, touch command also outputs the same error massage in some lines.
Any help is appreciated :)
I found out the problem, it was failing because instead of make a directory named as the variable $ExecutionNumber i made a file named as $ExecutionNumber.
I didn't realized it because i was expected something like "no such file or directory".
How curious, i thought that when you specify a path to a file or directory bash only evaluates if the last name is a file or a directory, supposing all the previous names should be directories.
I'll write it down later.
Thanks for the help #Barmar and #Gordon Davisson :)
I am having difficulty running a 1300 line bash script in sudo crontab. Also note this is on Ubuntu 10.04.
I suspect there is a "hidden" timeout somewhere because the script seems to stop at exactly 3 minutes very consistently but "timeout" isn't listed in the crontab file:
MAILTO="my#email.com"
00 12 * * * /path/to/script.sh
The script runs fine if I run it in a terminal window OR if I add >> anOutputFile.txt to the end of the crontab line. I could just use that as my solution but I want to understand what is causing the script to stop.
It almost seems like there's a timeout on the run time of the crontab script because it doesn't always stop at the exact same spot, but it does end within the same few lines consistently. I've tried adding a sleep 300 at the beginning of the script to see if it's really a timeout issue but that does not seem to be the case because it will still stop within the same few lines.
If it isn't a timeout issue, I am thinking there may be an issue with the for i in loop that the script fails around. If I comment out a the last two echo lines in the for i in {1..5} loop, the script finishes fine but if only one is commented out, it seems to be right at that unknown timeout limit and will have a 50-50 chance of finishing.
if ( [ ${#modemusbpath0} != 0 ] ); then
if test -e ModemPath; then
. /path/to/ModemPath
echo LEGACY MODEM USB PATH: $modemusbpath
echo LEGACY MODEM USB PATH: $modemusbpath >> /home/path/to/file/genrpt$DATESTR.log
BUS=`echo $modemusbpath | awk -F / '{print "/dev/bus/usb/"$5"/"}'`
echo BUS=$BUS
DEVICE=`echo $modemusbpath | awk -F / '{print $6}' | sed 's/^0*//' `
for i in {1..5} ; do
DEVICES=$(printf %03d $(($DEVICE + $i )) )
echo DEVICES=$DEVICES
NEXTMODEMPATH=$BUS$DEVICES
echo NEXTMODEMPATH=$NEXTMODEMPATH
echo "NEXTMODEMPATH=$NEXTMODEMPATH" >> /home/path/to/file/genrpt$DATESTR.log
#
modemusbpath="$modemusbpath $NEXTMODEMPATH"
done
echo modemusbpath=$modemusbpath
/home/path/to/file/TelemetryFailureReport.sh "TELEMETRY: NO CURRENT MODEM PATH; USING PATHS: $modemusbpath"
else
echo LEGACY MODEM PATH DOES NOT EXIST!
echo "LEGACY MODEM PATH DOES NOT EXIST!" >> /home/path/to/file/genrpt$DATESTR.log
fi
fi
Should I be specifying an output file? I think that solves my issue but I'd like to understand the cause.
A few more notes:
- I haven't been able to make MAILTO="my#email.com" work by setting it in the crontab in attempt to see the output, and even if I did, this script is used when there is no internet connection so an email shouldn't work.
- Also, I am using "timeout" as a variable at other points in the script, but there is no /bin/timeout or /sbin/timeout or /usr/bin/timeout or /usr/sbin/timeout. Should I stop using this as a variable name?
- PATH is the same between the terminal and a cron script.
Thank you.
The usual if format is a bit different. Can you try instead of this:
if ( [ ${#modemusbpath0} != 0 ] );
to be this:
if [ "${#modemusbpath0}" -ne 0 ];
i want to run a script via crontab, but I get an error message and don't know how to fix it.
It works fine if I run it in the shell.
Code:
#$/bin/bash
DIR=/var/log
REC="foo#bar.com foo#bar.de"
TOTALFILES="$(/home/pi/scripts/count-files-dirs $DIR | cut -d' ' -f2)"
#echo "$TOTALFILES"
if (( "$TOTALFILES" > 36 )); then
echo -e "Subject:$(date +%A' '%d' '%B' '%G) PI-07 var/log status \n\n $(/usr/games/cowsay moh moh moh)\n" | sendmail $REC
else
true
fi
Error:
/home/pi/scripts/check-files: 12: /home/pi/scripts/check-files: 48: not found
I tried to find the issue, but I don't know where it is.
The first line in your script should be:
#!/bin/bash
Try to define the sendmail path. You can find the path with
'whereis sendmail'
almost like you did with cowsay.
The error indicates something in this file was not found, would need to see the content of this file to be able to tell (or make a guess).
/home/pi/scripts/check-files: 48: not found
I'm new to scripting.
I downloaded cygwin and Notepad++(I'm using unix option-for writing and saving the ".sh files")
I have below script
Below code is from command $ cat -v pinging.sh
#!/bin/bash
target=$1
# email report when
SUBJECT="Ping failed"
EMAILID="someemailid#gmail.com"
count=$( $SYSTEMROOT/system32/ping -n -c 1 $target | grep 'received')
if [ $count == 0 ];
then
echo "Host : $target is not Alive!! Try again later.. at $(date)" | mail -s "$SUBJECT" $EMAILID
else
echo "Yes! Host is Alive!"
fi
done
But my script is giving error -
$ ./pinging.sh www.google.com
./pinging.sh: line 9: [: ==: unary operator expected
Yes! Host is Alive!
./pinging.sh: line 17: syntax error near unexpected token `done'
./pinging.sh: line 17: `done'
I'm not sure what I am doing wrong here.
The script is giving error
I'm getting- "host is alive" message always even in case of destination unreachable messages too. If I'm using ping www.somesite.com and if I'm getting destination unreachable through cygwin or cmd, this code is giving host is alive.
I also tried if [ $count -et 0 ]; in above code
Please help me!
Best Regards,
The value of the $count variable is not a number. It is a full line of text.
When you expand it in the [ test (without quotes) it gets word-split by the shell and the contents of the [ test become invalid (too many words) and you get your error.
If you quote "$count" you will avoid the error (but still not get the results you want).
You need to filter out only the number from the ping output and then use that in your [ test.
Add set -x to the top of your script to see the commands that are actually being run and you'll see the problem.
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. :)