I have below script. When I run each line in iTerm on MacOs, every command works. But if i save this as a shell script, it says "!! command not found". I have tried #!/bin/bash. But still it doesn't work.
#!/bin/sh
./ongoingShellScript.sh
if !! | grep "errors: 0"
then
echo Success
else
echo Failure
fi
I could have done
if ./ongoingShellScript.sh | grep "errors: 0"
But in this case, output of ongoingShellScript won't be printed in realtime.
What am i doing here ?
Thank you in advance
GV
!! doesn't refer to previous output -- it runs the whole command over again, and thus generates a new set of output. Moreover, the featureset it comes from -- called "history expansion" -- is an interactive extension turned off by default during script execution.
If you want to print status for the user while testing stdout for a string, the easy tool for the job is grep:
if ./ongoingShellScript.sh | tee /dev/stderr | grep -q "errors: 0"; then
echo "Success" >&2
else
echo "Failure" >&2
fi
...assuming that errors: 0 comes at the end of output, and thus that it's acceptable for tee to exit as soon as grep has seen this string.
Related
I am trying to check whether a docker container exists using grep.
The following script signals an error exit during running when IMAGE is empty and is fine when IMAGE is set. I can't find out the cause.
#!/usr/bin/env bash
set -e
IMAGE=$(docker ps | grep my-dev-image)
if [[ -z "$IMAGE" ]]; then
echo "It is not there"
else
echo "It is there"
fi
When you use set -e in a script the shell will exit whenever a command fails. It interacts badly with your grep call because grep exits with an error code if it doesn't find a match. If grep fails then the entire IMAGE=$(...) assignment fails and the script exits instead of setting IMAGE to an empty string.
You can fix this by ensuring the assignment always succeeds. A common idiom for this is to append || :. Adding || cmd will run cmd whenever the command on the left fails. And : is a command that always succeeds. (Yes, it's a command name consisting of a single colon. Strange, but it's a legal identifier.)
IMAGE=$(docker ps | grep my-dev-image) || :
Alternatively, you could check grep's exit code directly by using it in the if statement. This is what I would do if I didn't care about grep's output:
if docker ps | grep -q my-dev-image; then
echo "It is there"
else
echo "It is not there"
fi
Below is the sample script which i am trying to execute; but it fails to fetch the exit status of $cmd; is there any other way to fetch its exit status..!?
cmd="curl -mddddddd google.com"
status=$($cmd | wc -l)
echo ${PIPESTATUS[0]}
I know that, if i replace status=$($cmd | wc -l) with $cmd | wc -l , i could fetch the exit status of $cmd using PIPESTATUS. But in my case i have to assign it to a variable (example: status in above case).
Please help me here..!
Regards,
Rohith
What you're assigning to the status variable is not a status, but what $cmd | wc -l pipeline prints to standard output.
Why do you echo anyway? Try realstatus=${PIPESTATUS[0]}.
EDIT (After some digging and RTFMing...):
Just this -- realstatus=${PIPESTATUS[0]} -- doesn't seem to help, since $(command_substitution), which is in your code, is done "in a subshell environment", while PIPESTATUS is about "the most-recently-executed foreground pipeline"
If what you're trying to do in this particular case is to ensure the curl (aka $cmd) command was succesfull in the pipeline you should probably make use of pipefail option (see here).
If the output of the command is text and not excessively large, the simplest way to get the status of the command is to not use a pipe:
cmd_output=$($cmd)
echo "'$cmd' exited with $?"
linecount=$(wc -l <<<"$cmd_output")
echo "'wc' exited with $?"
What counts as "excessively large" depends on the system, but I successfully tested the code above with a command that generated 50 megabytes (over one million lines) of output on an old Linux system.
If the output of the command is too big to store in memory, another option is to put it in a temporary file:
$cmd >tmpfile
echo "'$cmd' exited with $?"
linecount=$(wc -l <tmpfile)
echo "'wc' exited with $?"
You need to be careful when using temporary files though. See Creating temporary files in Bash and How create a temporary file in shell script?.
Note that, as with the OP's example code, the unquoted $cmd in the code examples above is dangerous. It should not be used in real code.
If you just want to echo the pipe status, you can redirect that to stderr. But you have to do it in the subshell.
status=$($cmd | wc -l; echo ${PIPESTATUS[0]} >&2)
Or you can capture both variables from the subshell using read
read -rd $'\0' status pstatus <<<$($cmd | wc -l; echo ${PIPESTATUS[0]})
I have a scenario to copy file from one server to another, for that i need to check any existing scp is in progress, have wrote a sample shell script but the condition is not being met even though syntax is correct, the main problem here is the output of ps command will gets stored in variable scpstat and the same compared for matching string in if statement, here I'm getting the output of the variable is different from executing outside of the script. can see it is formatted different in script execution when executing sh -x scpsamp.sh, why there is "sh" appended to the output, but while comparing without ps and assigning as scpstat='scp' i can able to get the condition correct, am i doing anything wrong while getting output in to the variable. please help
#!/bin/sh
scpstat=`ps -ef | grep scp | egrep -v 'grep|ssh' | awk '{print $8}')`
if [ "$scpstat" = "scp" ];
then
echo "SCP is in progress"
else
echo "No SCP in progress"
fi
sh -x output
It's notoriously difficult to extract information from the output of ps. If your system has pgrep, it's much easier:
if pgrep scp >/dev/null
then
echo "SCP is in progress"
else
echo "No SCP in progress"
fi
I'm writing a unix shell script and need to check if there are currently running processes with "xyz" in their directory. If yes than continue to next command and show text like "Found It".
If not than don't continue and display text like "Process Not Found".
I tried something like this:
if ps -ef | grep xyz
then
echo "XYZ Process Found!"
else
echo "XYZ Process Not Found!"
fi
But it just showing me the processes and display "process found" even if there's no xyz process.
I believe you want to check the output of the command against a value using Command substition, from the linked bash-hackers wiki The command substitution expands to the output of commands. These commands are executed in a subshell, and their stdout data is what the substitution syntax expands to. Also, count the lines and remove grep. Something like,
if [[ $(ps -ef | grep xyz | grep -v grep | wc -l) != 0 ]]; then
echo "XYZ Process Found!"
else
echo "XYZ Process Not Found!"
fi
Edit
Based on the comments below, you should probably use
if [[ $(ps -ef | grep -c xyz) -ne 1 ]]; then
which is a lot easier to read.
When you run grep xyz, that process - grep xyz - is also running & thus shown in the output of ps -ef.
This running process command line contains xyz. Thus grep passes that line to output.
Hence you always get zero exit status - i.e. success.
2 Solutions:
use if ps -ef | grep '[x]yz'; then. (You may want to suppress grep output with -q)
The grep command being run is grep [x]yz. This gets printed in ps -ef output.
Obviously, grep filters out this line. [x]yz could be matched with \[x\]yz, not with [x]yz.
use if pgrep -f xyz >/dev/null; then
Check man pgrep for more details..
You can also use pgrep. From pgrep(1):
pgrep looks through the currently running processes and lists the
process IDs which match the selection criteria to stdout.
[...]
EXIT STATUS
0 One or more processes matched the criteria.
1 No processes matched.
2 Syntax error in the command line.
3 Fatal error: out of memory etc.
Example output:
[~]% pgrep xterm
18231
19070
31727
You can use it in an if statement like so:
if pgrep xterm > /dev/null; then
echo Found xterm
else
echo xterm not found
fi
Note: pgrep is not a standard utility (ie. it's not in POSIX), but widely available on at least Linux and I believe most BSD systems.
is_xyz_running() {
[ "$(pgrep xyz)" ] && echo true || echo false
}
I implemented the following in POSIX shell (not bash):
fail.sh:
#!/bin/sh
echo something useful
echo warning 1 >&2
echo warning 2 >&2
echo an error message >&2
exit 100
The command prints something I want to use on stdout, some warnings on stderr and an error message on stderr as well before failing with exit code 100.
success.sh:
#!/bin/sh
echo something useful
echo warning 1 >&2
echo warning 2 >&2
exit 0
This command prints something to stdout and some warnings to stderr but finishes successfully with exit code 0.
test.sh:
#!/bin/sh -e
script=$1
rm -f success
msg=$({ $script > useful; touch success; } 2>&1 | tail -1;)
if [ -f success ]; then
echo success
else
echo failure
echo last error was: $msg
fi
In this script I want to run either of those two scripts and provide the following functionality:
the output of the scripts must be redirected to a file
the last line of stderr must be saved to a variable so that I can print that last line later in case the command didnt exit successfully
I want to detect whether or not the command exited successfully by checking its exit status
My script test.sh achieves all of that but it uses an external file. Since I use -e the touch will only be executed if $script executed successfully. Can I capture the exit code of $script without this technique?
The script must be written in POSIX shell and must use -e.
#!/bin/sh -e
script=$1
if msg=$($script 2>&1 >useful); then
echo success
else
echo failure
msg=$(echo "$msg" | tail -1)
echo last error was: $msg
fi