Linux terminal one-liner command to evaluate the response of an HTTP request - linux

I'm guessing this is a very simple question, sorry about that.
I need to execute a command based on the response of an HTTP request. It's just that it has to be a single line of command and no bash script (by that I mean a separate bash script file).
Here's a more concrete example. I have a local API that returns an integer if it is up and running:
$ curl -s http://localhost
1
Of course, for whatever reason the server might be down in which case the above command will return an empty string. Or it might be up but it returns 0. In either of these two cases, I need to execute a command to mitigate this situation (if you are interested, I'll be executing exit(1)). Otherwise, if the API returns 1 or a larger number, I don't need to do anything.
Can someone please help me come up with a one-liner for this? Thanks.

Use command substitution to get the returned value as a string, then you can compare it inside the test command.
Here's a one-liner, but it only recognizes 1 as a valid answer:
[ "$(curl -s http://localhost)" = "1" ] || exit 1
Here's one that allows any value at least 1 as valid, but I can't write it as a one-liner.
var=$(curl -s http://localhost)
if [ -z "$var" ] || [ "$var" -eq 0 ]
then exit 1
fi
I don't know how to do this as a one-liner, because it needs to do two different tests on the result: a string test to check for an empty result, and a numeric test for 0. That requires assigning to a variable or doing repeated curl requests.

Related

Dynamically generate command in bash

I want to dynamically generate pretty long bash command depending on the command line options. Here is what I tried:
CONFIG_PATH=""
#Reading CONFIG_PATH from getopts if supplied
SOME_OPT=""
if [ ! -z "$CONFIG_PATH" ]; then
SOME_OPT="-v -s -cp $CONFIG_PATH"
fi
some_bash_command $SOME_OPT
The point here is that I want to pass 0 arguments to the some_bash_command if no arguments were passed to the script. In case there were some arguments I want to pass them.
It works fine, but the problem is that this approach looks rather unnatural to me.
What would be a better yet practical way to do this?
Your approach is more-or-less the standard one; the only significant improvement that I'd recommend is to use an array, so that you can properly quote the arguments. (Otherwise your command can horribly misbehave if any of the arguments happen to include special characters such as spaces or asterisks.)
So:
SOME_OPT=()
if [ ! -z "$CONFIG_PATH" ]; then
SOME_OPT=(-v -s -cp "$CONFIG_PATH")
fi
some_bash_command "${SOME_OPT[#]}"

Accessing the value returned by a shell script in the parent script

I am trying to access a string returned by a shell script which was called from a parent shell script. Something like this:
ex.sh:
echo "Hemanth"
ex2.sh:
sh ex.sh
if [ $? == "Hemanth" ]; then
echo "Hurray!!"
else
echo "Sorry Bro!"
fi
Is there a way to do this? Any help would be appreciated.
Thank you.
Use a command substitution syntax on ex2.sh
valueFromOtherScript="$(sh ex.sh)"
printf "%s\n" "$valueFromOtherScript"
echo by default outputs a new-line character after the string passed, if you don't need it in the above variable use printf as
printf "Hemanth"
on first script. Also worth adding $? will return only the exit code of the last executed command. Its values are interpreted as 0 being a successful run and a non-zero on failure. It will NEVER have a string value as you tried to use.
A Bash script does not really "return" a string. What you want to do is capture the output of a script (or external program, or function, they all act the same in this respect).
Command substitution is a common way to capture output.
captured_output="$(sh ex.sh)"
This initializes variable captured_output with the string containing all that is output by ex.sh. Well, not exactly all. Any script (or command, or function) actually has two output channels, usually called "standard out" (file descriptor number 1) and "standard error" (file descriptor number 2). When executing from a terminal, both typically end up on the screen. But they can be handled separately if needed.
For instance, if you want to capture really all output (including error messages), you would add a "redirection" after your command that tells the shell you want standard error to go to the same place as standard out.
captured_output="$(sh ex.sh 2>&1)"
If you omit that redirection, and the script outputs something on standard error, then this will still show on screen, and will not be captured.
Another way to capture output is sending it to a file, and then read back that file to a variable, like this :
sh ex.sh > output_file.log
captured_output="$(<output_file.log)"
A script (or external program, or function) does have something called a return code, which is an integer. By convention, a value of 0 means "success", and any other value indicates abnormal execution (but not necessarily failure) : the meaning of that return code is not standardized, it is ultimately specific to each script, program or function.
This return code is available in the $? special shell variable immediately after the execution terminates.
sh ex.sh
return_code=$?
echo "Return code is $return_code"

linux shell - strange 'if'?

I am reading a source code, and find these lines :
if [ -n "${INIT_NAMENODE+1}" ]
then
echo "Initializing namenode"
else
echo "Starting namenode"
fi
how should I interpred the 'if' condition : if [ -n "${INIT_NAMENODE+1}" ] ? ?
The nice thing about this code is that it is not written for a "Linux shell". It is written for the more general category of "UNIX shell". It will work in everything since V7 UNIX (1979) at least. People with lesser portability goals might write it without the -n.
The first item of interest is the ${foo+bar} syntax. This is a test for existence of the foo parameter. If $foo exists, then ${foo+bar} equals bar. If $foo doesn't exist, then ${foo+bar} equals the empty string.
If you look for this in your shell man page, it's usually documented as ${foo:+bar}, along with some other related forms like ${foo:-bar}, and somewhere nearby there's a note explaining that the colon can be omitted from all of them, resulting in slightly different behavior (with the colon, variables whose value is the empty string are treated the same as nonexistent variables).
Next we have the [ -n ... ] test. -n tests the following string for emptiness. It succeeds if the string is non-empty. From the previous paragraph we know that ${INIT_NAMENODE+1} is empty if and only if $INIT_NAMENODE doesn't exist. So the -n test succeeds if $INIT_NAMENODE exists. The value 1 doesn't really matter here - it would do the same thing if you changed the 1 to 2 or 0 or teapot. All that matters is that it's not an empty string, since -n doesn't care about the rest.
Try some examples from your shell prompt: echo ${PATH+hello} should say hello because you do have a $PATH variable. echo ${asdfghjkl+hello} should print a blank line.
So, in the context of the if statement, the purpose of the test is to do the first echo if the variable $INIT_NAMENODE exists, and the second echo if the variable $INIT_NAMENODE doesn't exist.

Passing IPC(Instructions/Cycles) continuously to other function or variable

I am trying to read the performance counters and get the IPC. I need to use IPC to control few machine specific parameters. I am using shell script to do the same. Please see the code below:
while true
do
retval=./perf periodic -e instructions -e cycles -s 50 -d -m td &
some_pid=$!
kill some_pid
if ["$retval" -gt "0.5"]
then
***something***
fi
sleep 1
done
I am getting following error:
Algorithm.sh[27]: kill: some_pid: arguments must be jobs or process IDs
Algorithm.sh[27]: periodic: not found
Algorithm.sh[27]: [: missing ]
Algorithm.sh[27]: kill: some_pid: arguments must be jobs or process IDs
Algorithm.sh[27]: [: missing ]
Algorithm.sh[27]: periodic: not found
Algorithm.sh[27]: kill: some_pid: arguments must be jobs or process IDs
Algorithm.sh[27]: [: missing ]
Can someone give me some pointers on how to get/return the value from perf instruction. I tried using function and returning the value, but it also failed.
---------UPDATED----------
Now I am running following, and one of the problem got solved and one is remaining.
./perf periodic -e instructions -e cycles -s 50 -d -m td > result.txt &
And other one is
while true
do
retval=$(tail -n 1 result.txt)
echo $retval
if ["$retval" -gt "0.5"]
then
echo "Hello mate"
fi
sleep 1
done
The echo is giving value, but then the if statement is not getting executed. It is giving following:
Algorithm.sh[30]: [: missing ]
0.302430
Algorithm.sh[30]: [0.302430: not found
0.472716
Algorithm.sh[30]: [0.472716: not found
0.475687
Algorithm.sh[30]: [0.475687: not found
I looked up the if condition syntax and couldn't spot the mistake. Please help.
Couple of shell syntax issues here.
First, retval=... is going to set the retval variable equal to the first part of the string on the right side of the '='. The ampersand will then background the whole thing, essentially throwing that value away. You probably meant to do:
retval=`./perf periodic -e instructions -e cycles -s 50 -d -m td`
which would store the output of the perf command into retval. However, that won't work if you put it into the background with '&'. You'll need to either (a) run it synchronously without the '&' as I've shown above, (b) redirect its output into a file and recover it after it's finished (you'll need to use wait to determine when that's happened), or (c) use a "coprocess" (too complicated to explain here: see the bash man page).
Also, you probably meant kill $some_pid? Without the '$', the string "some_pid" is passed as a literal argument to kill, which is probably not what you intended.
Edit
Following your revisions... The shell operates by splitting the command line up into individual tokens. So spaces are often important. In this case, the initial token being identified by the shell will be the combined value of ["$retval" (after variable substitution and quote removal). The last token will be 0.5] after removal of quotes. In the first invocation line then, the first token was simply '[' (presumably retval was empty the first time through). So there it's complaining about the last token not being the matching ']'. In the other iterations, the first token is '[' plus additional numeric text from $retval which is not providing a valid command name.
Once you fix that, you'll discover that the -gt operator only evaluates integer comparisons. You could use the bc(1) command. For example, this command will produce output of 1 if $retval is greater than 0.5; otherwise 0.
echo "$retval > 0.5" | bc
But note you'll need to ensure retval has a valid numeric expression or you'll cause a syntax error in bc. You would then need to capture the output and put it into a conditional. Something like this should work:
if [ "$retval" ]
then
x=$(echo "$retval > 0.5" | bc)
if [ $x -eq 1 ]
then
echo "hello mate"
fi
fi
(Note that with $(...) you don't need additional spaces next to the parentheses. And in the assignment statement x=foo, you must not have a space on either side of the =.)

Linux and wget.. specify alternate link

Is it possible to make (via Linux command line tools ) wget download from an alternate link in case the download fails ?
Example:
Download file.zip from http://www.secondary.com/file.zip in case it's not found at http://www.primary.com/file.zip.
You can use a shell construct like this.
wget http://www.primary.com/file.zip || wget http://www.secondary.com/file.zip
The || is the OR operator, and depends on the fact that it "short circuits" the evaluation if the whole statement is true. This is a functional style, where the first statement is evaluated and if it's "true" (returns zero) then the second is not evaluated. If it's "false", the second is evaluated. The side effect of evaluating these commands is downloading the file.
Try something like this
wget_exit=$(wget "$MYURL")
if [ $? -ne 0 ];
then
wget "$MYALTURL"
fi
The exit status is captured in $?. You could also treat specific errors differently by looking at the exit statuses of wget here.

Resources