test command in unix doesn't print output - bash

Why is there no output to the command test 3 -lt 6 in the unix terminal? Shouldn't test output a 0 or a 1?
I did a man test and it says
Exit with the status determined by EXPRESSION

The exit status is not printed, it is just returned. You can test it in if or while, for example
if test 3 -lt 6 ; then
echo ok
else
echo not ok
fi
Moreover, the exit code of the last expression is kept in $?:
test 3 -lt 6; echo $?

test returns an exit status, which is the one that indicates if the test was succesfull. You can get it by reading $? right after executing test or you can use it directly in a control structure, for example:
if test 3 -lt 6
do echo "everything is aaaaalright"
else echo "whaaat?"
fi

Every command in the shell has a return value, not just test. Return values are rairly printed, we test them directly, for example with an if or while statement, or maybe use it in a shortcut with && or ||. Sometimes we might store the return value, which is also available in the variable ?.
The test command is rather ancient. Since you have tagged bash then you might wish to consider using
if (( 3 < 6 )); then
for arithmetic tests, or
if [[ $name == "Fred Bloggs" ]] ; then
for textual or pattern tests.
These are generally more intuitive and less error prone. Like test, (( )) and [[ ]] are commands in their own right, and can be used without if or while. They return 0 (success) on true, and 1 (failure) on false.
By the way, when you do a man test you are actually looking at the doc for the test program. You might be better using man bash or help test.

Related

Verifying bash script inputs

I've just started using Linux as part of my computer science degree.
I'm writing some very simple Bash scripts and I've become a tad bit stuck.
I would like the script I'm attempting to write to be able to differentiate between "non valid inputs ie letters" from "valid inputs ie numbers from a specific range"
Currently the script "works" although I'm having troubles with another echo that I would like only to "echo" when the below line is "not true", is there a simple way to write this? I'm not specifically looking for efficient code, just code that I can learn from and understand at my amateur level.
So, long story short, is it possible to obtain information from the command line below, so that I can have a simple "not true" variable that I can use in another "else" or "elif" command?
For reference line 1 is to detect alphabetical inputs, and line 2 being the line of code I would like to write as "not true" for use in another part of my script.
let xx=$a+1-1 2>/dev/null; ret=$?
if [ $a -ge 7 ] && [ $a -le 70 ] && [ $xx -eq $xx ] && [ $ret -eq 0 ]
I'm not sure I'm explaining it very well, so any help would be appreciated. :)
Welcome to Stack Overflow. :)
Start by reading the docs. I don't mean that in any way to be mean - it's just the best way to go about this.
c.f. this manual
Then read through the BashFAQs
Also, this site is really your friend. Start by familiarizing yourself with how to ask a question well.
For your question, if I read it right:
typeset -i xx # accepts only digits now.
If the input is foo, the value defaults to 0, so now just check the range.
if (( xx >= 7 && xx <= 70 )); then : value is ok
else echo "Value must be a number from 7 to 70"
exit 1
fi
Good luck. :)
One problem with the "variable with integer attribute" is that it still doesn't protect you from invalid input:
$ declare -i aNumber
$ aNumber=1234X
bash: 1234X: value too great for base (error token is "1234X")
See 6.5 Shell Arithmetic for how bash interprets values to be numbers (scroll down to the paragraph starting with "Integer constants follow the C language definition")
In my experience, the best way to check for valid numeric input is with string-oriented pattern matching.
if [[ $1 =~ ^[+-]?[0-9]+$ ]]; then
echo "input $1 is an integer"
fi
In addition to extended regular expressions, bash's advanced pattern matching can also be used within [[...]]
if [[ $1 == ?([+-])+([0-9]) ]]; then
echo "input $1 is an integer"
fi
((...)) is preferred over let. See the let builtin
command for details.
Also the shellcheck wiki entry.

How to determine one program output to pipe as next program input in shell using ${PIPESTATUS[*]}

I am new to shell script. I have a scenario where i need to get the exit status of one shell script and pass its value as input(if exit code is zero/otherwise exit the main shell script file) to the next shell script and continue until it executes all the scripts. Below is the code i tried.but its not working as expected.
status=`run1.sh`
if [[ status -eq 0 ]]; then
status=`run2.sh`
else
exit 1
fi
if [[ status -eq 0 ]];then
status=`run3.sh`
else
exit 2
fi
Its running successfully for first 2 scripts. It's failing on second if block, even though the output value of run2.sh is 0. I searched in google, its suggesting to use {PIPESTATUS[#]}, I tried it by replacing exit 1 with pipestatus and pass pipestatus in the second if block like below.
status=`run1.sh`
if [[ status -eq 0 ]]; then
status=`run2.sh`
else
exit ${PIPESTATUS[1]} ## pipestatus[1]- is run2.sh output value
fi
if [[ ${PIPESTATUS[1]} -eq 0 ]];then
status=`run3.sh`
else
exit 2
fi
I think i am not clear on how to use pipestatus. I would appreciate if anyone can provide me some example to my scenario.
status is a static string; you are not examining the variable you created at all.
There is no pipe here so PIPESTATUS does not come into play. It's for code like
one | two
where traditionally the exit status of one wasn't available to the shell; Bash changed that by exposing results from every process in a pipeline.
Having your scripts print the number zero to signal success is not how it's usually done. A command produces both output and a status code (the number you give as the argument to exit, or in a function, to return), and code would usually examine the latter. It is exposed in $? but the shell's control structures implicitly check it under the hood, so your code would look like
if run1.sh; then
if run2.sh; then
if run3.sh; then
...
provided you change them to produce a useful exit code. Though this could be further simplified to
run1.sh || exit
run2.sh || exit
run3.sh || exit
or even to
set -e
run1.sh
run2.sh
run3.sh

KSH: IF condition issue: if test .... then vs if ... then

what is the difference if I put test or without test in a .ksh file?
if test $1 -ne 0 ; then ....
and
if $1 -ne 0 ; then ....
Many Thanks
I actually think this is an important question, as it highlights some important rules in shell programming:
Every command in the shell returns an true (0) or false exit code
Control structures in the shell do not require comparisons
Every command in the shell returns an exit code
Any properly coded command in the shell will return 0 for success,
and non-zero for failure. While there is only one way to succeed, but always more than way to fail.
Example:
$ no-such-command || echo no $?
ksh[1]: no-such-command: not found [No such file or directory]
no 127
$
The exit status of a command is caught in the pseudo variable $? and is available until you complete another command.
This exit status is used in control structures like if ... then ... fi
or until ... do ... done.
failing(){ return 2; }
failing &&
echo "It works" ||
echo "It failed with exit code $?"
results in
It failed with exit code 2
Control structures in the shell do not require comparisons
Let's start with the simplest definition
of the if command:
if compound-list
then
compound-list
fi
For the full syntax, see Section 2.9.4 Compound Commands of Shell Command Language of The Open Group Base Specifications.
Between the keywords, if, then, and fi there are two sections of
code, named compound-list.
This is shorthand for any sequence of code that would be valid in a script. The exit status of the list will be equal to the exit status of the last command in the list.
The important difference for the two lists is that the firts will determine the flow of control, while the second determines the exit status of the entire expression, when executed.
Conclusion
Any command can be used as the test in an if/then/else/fi construct.
Because we often want to test things explicitly, we often use the actual test command or its derivatives [ ... ] and [[ ... ]].
if [[ -n $1 ]]; then
echo "'$1' is a non-empty string"
fi
For complex expressions it is always preferred to wrap them in a
function to apply some abstraction.
One more trivial example:
non_empty_files_present(){
(path=${1:?directory expected}
(find ${path} -type f -size +0 | read line) 2> /dev/null
)
}
if non_empty_files_present /var/tmp; then
echo "Some files have content"
fi

Increase value variable bash

I want to increase the var upPCs by one every time I go through the if statement.
upPCs= 0;
if [ $? -eq 0 ]; then
$upPCs ++;
fi
This doesn't work.
Bash (and other shells) do not allow spaces around the equals sign. A semi-colon ; is used to separate commands on a line, so putting one at the end of the line is a waste (unless your really enjoy typing semi-colons...).
upPCs=0
Use (( )) for arithmetics:
if (( $? == 0 )); then
(( upPCs++ ))
fi
Finally, you should indent your code inside if (and while, for, etc) statements, because if you don't a kitten dies. Seriously, scripts can get unreadable quite quickly if you do not indent - it is worth it.
By the way, often you don't need to test $? explicitly. You don't show the command that you are testing the result of, but the principle is that if is true if the command is successful (exit code of zero is considered success). For example:
if some_command
then
(( upPCs++ ))
fi

assign variable inside if condition in bash 4?

is it possible to assign variable inside if conditional in bash 4? ie. in the function below I want to assign output of executing cmd to output and check whether it is an empty string - both inside test conditional. The function should output
"command returned: bar"
myfunc() {
local cmd="echo bar"
local output=
while [[ -z output=`$cmd` ]];
do
#cmd is failing so far, wait and try again
sleep 5
done
# great success
echo "command returned: $output"
}
why the above?
i prefer to run scripts with 'set -e' - which will cause script to terminate on first non-0 return/exit code that's not in an if/loop conditional.
with that in mind, imagine cmd is an unstable command that may exit with > 1 from time to time, and I want to keep calling it until it succeeds and i get some output.
You can try something like this:
myfunc() {
local cmd="echo bar"
local output=
while ! output=$($cmd) || [[ -z output ]];
do
#cmd is failing so far, wait and try again
sleep 5
done
# great success
echo "command returned: $output"
}
Note that it is strongly recommended to avoid the use of set -e.
I don't think you would be able to do it in your conditional
As yi_H pointed out, the if is equivalent to
if [[ ! -z output=bar ]];
which in turn is basically
if [[ ! -z "output=bar" ]];
So, all you are checking is if the string "output=bar" is empty or not...
So, output=bar could actually be anything like !##!#%=== and it would still do the same thing (that is, the expression isn't evaluated). You might have to assign the variable in a subshell somehow, but I'm not sure that would work.
Since assignment won't work there, you need some workaroudn.
You could temporary do a set +e...
You could use this way ...
$cmd
exit_status=$?
while [[ $exit_status -gt 0 ]];
do
#cmd is failing so far, wait and try again
sleep 5
$cmd
exit_status=$?
done
EDIT: This won't work with 'set -e' or other way around, don't use 'set -e' to begin with.

Resources