Comparing result, input and output of tests in shell - bash

Basically what I want to do is write a shellscript that will run series of test, take in any input (if there is any) and compare the test result with an output.
So my file looks something like this.
#!/bin/sh
# Let's set our working directory
DIR="./tests"
# And create some variables
passed=0
failed=0
totalamount=0
returned=0
# Inform the user about start of testing
echo "========= TESTING INITIALIZED ========="
# Actual testing I'm trying to do
So I assume first thing I should do is create a loop that will run through the tests
for file in "./tests/1/*.test do
So Now I'm unsure how can I compare the output of my .test file with a prepared .output program.
For example, I have a program test1.test, that will calculate 2+3. The result is 5. What I want is to have this value stored and compare it with my test1.output
Any idea how to do this?
In the end I'll just compare the two values
if [ $returned -eq $expected ]; then
echo "Test was succesful"
else
echo "Test was unsuccesful"
fi
Basically the end result should look something like:
Test: test1.test
Expected result: X
Your result: Y
Test was succesful/unsuccesful

You could use diff to compare the test result with the output expected. If they match, then you could assume the test was successful. For example:
test1check="$(diff /folder/test1 /folder/test1-expected)"
if [ -z "$test1check" ]; then
echo "Test was succesful"
else
echo "Test was unsuccesful, difference on results: $test1check"
fi
If the user is going to make inputs, or in other cases at your discretion, you could run the same comparison but ignore case differences (ex: "APPLE" would match "apple"), by changing the first line like this:
test1check="$(diff -i /folder/test1 /folder/test1-expected)"
One more thing: keep in mind that this code is sensitive to newlines, so if the test result does not end in a new line and the expected result does, the script will point out the difference.

Related

use array in bash for change name of created folder in each loop

I am trying to do a script in bash and the idea is that I have an array with text and I use to creat a folder, for example:
#!/bin/bash
dir=(dir1 dir2 dir3)
for i in 0 1 2; do
d=run_${#dir[i]}
echo "Prepare case ${d}..."
done
My problem is that when I do this, it prints:
Prepare case run_4...
Prepare case run_4...
Prepare case run_4...
and the number 4 corresponds to the lenght of each array element, (if I change dir1 to direc1 for example, I get in the first line of the output "Prepare case run_6...")
What i was looking:
Prepare case run_dir1...
Prepare case run_dir2...
Prepare case run_dir3...
What i am missing?
Remove # from this commnd d=run_${#dir[i]}
And you can loop over array values like this:
for i in "${dir[#]}"; do
echo "Prepare case run_$i..."
done

Strange Behavior in Bash For Loop

Given the following code, BASH's output is unexpected for me and I'm looking for possible solutions (I've tried changing the way I'm quoting but that doesn't seem to affect anything to produce the desired result):
Testing File:
#!/bin/bash
. FindMissingSettings.function
Settings[0]="FirstSetting"
Settings[1]="SecondSetting"
Settings[2]="ThirdSetting"
ThisFile="ThisFile"
find_missing_settings "${Settings[#]}" "$ThisFile"
The included FindMissingSettings.function:
#!/bin/bash
find_missing_settings () {
Settings=("$#")
File=$2
for Setting in "${Settings[#]}"; do
echo "$Setting"
echo "1"
done
echo "$File"
echo "2"
}
I expected the output from this script and the included function to be:
FirstSetting
1
SecondSetting
1
ThirdSetting
1
ThisFile
2
However this was the result I received:
FirstSetting
1
SecondSetting
1
ThirdSetting
1
ThisFile
1
SecondSetting
2
Why is this and what can I do to provide the desired result? Thank you!
In your find_missing_settings function, in your variable Setting, you have all the given inputs (FirstSetting, Second Setting, ThirdSetting, ThisFile). That's why it print it all with during the loop.
Then it print the 2nd setting in the list, so SecondSetting
To fix this, you can put ThisFile as first parameter of the function:
find_missing_settings "$ThisFile" "${Settings[#]}"
And change in the find_missing_settings function how you get the inputs:
Settings=("${#:2}")
File=$1
The :2 ask to get the inputs starting from the 2nd one only, and you put the first one (ThisFile) in the variable File

Bash Function is not getting called, unless I echo the return value

In my program I am trying to return a value from a function, the return value is string. Everything works fine(atleast some part), if I echo the value once it is returned, but it is not even calling the function, if I dont return.... Consider the code below....
#!/bin/bash
function get_last_name() {
echo "Get Last Name"
ipath=$1
IFS='/'
set $ipath
for item
do
last=$item
done
echo $last
}
main() {
path='/var/lib/iscsi/ifaces/iface0'
current=$(get_last_name "$path")
echo -n "Current="
echo $current
}
main
It gives me an output like this
OUTPUT
Current=Get Last Name iface0
If I comment the echo $current, then the I am not even seeing the "Get Last Name", which makes to come to conclusion, that it is not even calling the function. Please let me know what mistake I am making. But one thing I am sure, bash is the ugliest language I have ever seen.......
Functions do not have return values in bash. When you write
current=$(get_last_name "$path")
you are not assigning a return value to current. You are capturing the standard output of get_last_name (written using the echo command) and assigning it to current. That's why you don't see "Get last name"; that text does not make it to the terminal, but is stored in current.
Detailed explanation
Let's walk through get_last_name first (with some slight modifications to simplify the explanation):
function get_last_name () {
ipath=$1
local IFS='/'
set $ipath
for item
do
last=$item
done
echo "Get Last Name"
echo $last
}
I added the local command before IFS so that the change is confined to the body of get_last_name, and I moved the first echo to the end to emphasize the similarity between the two echo statements. When get_last_name is called, it processes its single argument (a string containing a file path), then echoes two strings: "Get Last Name" and the final component of the file path. If you were to run execute this function from the command line, it would appear something like this:
$ get_last_name /foo/bar/baz
Get Last Name
baz
The exit code of the function would be the exit code of the last command executed, in this case echo $last. This will be 0 as long as the write succeeds (which it almost certainly will).
Now, we look at the function main, which calls get_last_name:
main() {
path='/var/lib/iscsi/ifaces/iface0'
current=$(get_last_name "$path")
echo -n "Current="
echo $current
}
Just like with get_last_name, main will not have a return value; it will produce an exit code which is the exit code of echo $current. The function begins by calling get_last_name inside a command substitution ($(...)), which will capture all the standard output from get_last_name and treat it as a string.
DIGRESSION
Note the difference between the following:
current=$(get_last_name "$path")
sets the value of current to the accumulated standard output of get_last_name. (Among other things, newlines in the output are replaced with spaces, but the full explanation of how whitespace is handled is a topic for another day). This has nothing to do with return values; remember, the exit code (the closet thing bash has to "return values") is a single integer.
current=get_last_name "$path"
would not even call get_last_name. It would interpret "$path" as the name of a command and try to execute it. That command would have a variable current with the string value "get_last_name" in its environment.
The point being, get_last_name doesn't "return" anything that you can assign to a variable. It has an exit code, and it can write to standard output. The $(...) construct lets you capture that output as a string, which you can then (among other things) assign to a variable.
Back to main
Once the value of current is set to the output generated by get_last_name, we execute
two last echo statements to write to standard output again. The first writes "Current=" without a newline, so that the next echo statement produces text on the same line as the first. The second just echoes the value of current.
When you commented out the last echo of main, you didn't stop get_last_name from being executed (it had already been executed). Rather, you just didn't print the contents of the current variable, where the output of get_last_name was placed rather than on the terminal.

Calling external script in matlab and capturing output

Hey so I have a bash command that echos a string based on reading some file. Say for simplicity it is like this
for line in `cat file`
do
if [ "$line" == "IwantThisLine" ]
then
echo "True"
fi
done
And I have it saved as its own individual script. Its called readRef.sh. So now I want to call it in matlab and store whatever it outputs in a variable! I am not entirely sure on how to do that, I seem to get an error when using evalc() on a system(). But it could be just me messing up quotations.
I tried something like
evalc(system(['./readRef.sh ' bamfile']))
The "bamfile" is a variable that is just a string to the path of a bamfile.
I get this error.
>> tes = evalc(system(['./readRef.sh ' smplBamFile]))
hg18
??? Undefined function or method 'evalc' for input arguments of type 'double'.
Coincidentally it does spit out "hg18" which is what I want to set the matlab variable to be.
Oh, I see. I don't think you need evalc at all. Reading the system docs you can just do:
[status, result] = system('echo True; echo "I got a loverly bunch of coconuts"')
And result will be
True
I got a loverly bunch of coconuts
So just do:
[status, result] = system(['./readRef.sh ' smplBamFile])
The reason evalc isn't working is that it requires its input to be a Matlab expression in a string, but you are passing it the result of system.
You could try:
evalc("system(['./readRef.sh ' smplBamFile])")
See how I'm passing in the system(...) as a string?
The reason you get this error is because system(...) returns the return-code of the command it ran, not its output. To capture its output, use
[~, output] = system(...)
tes = evalc(output);

BASH: Assign '&' to variable NOT as string

I wanted to conditionally run a command as a background or foreground process, so I wrote something like this:
test $some_var; bg_suffix=&
long_command $bg_suffix
it doesn't work because bg_suffix is always empty whether it's been assigned or not.
But
test $some_var; bg_suffix="&"
long_command $bg_suffix
doesn't work either because now bg_suffix is interpreted as a string.
Any ideas how to solve this problem? Thanks!
Here is how to do it without using a quote-breaking eval
inBackground () {
t=$1
shift
if $t; then
"$#"&
else
"$#"
fi
}
This lets you do something like:
inBackground false echo '$$'
inBackground true sleep 4
This gets around the problem that all the eval-based solutions have: new and sometimes impossible quoting rules. For example, try to pass the '$$' through eval. Because true and false are not significant to the parser they can be in variables and things will still work.
Of course, if you wanted shell metachars to work (say, you redirect i/o) then eval is better, or you need to define a procedure for the command, and if you define a procedure, you problem is solved:
complicated_command () {
sleep 3
echo replace this with something complex
}
do_background=true
$do_background && (complicated_command&) || complicated_command
How about:
if [[ ${somevar} ]] ; then
long_command &
else
long_command
fi
or, if it is a long command you don't want to have to enter twice:
long_command=insert your big honking command here
if [[ ${somevar} ]] ; then
${long_command} &
else
${long_command}
fi
Just as an aside, I hope you're aware that the command sequence:
test ${condition}; x=2
will set x to 2 regardless of the test results. You may have meant to write:
test ${condition} && x=2
did you try
eval (long_command $bg_suffix)
using bg_suffix="&"
I don't know why I am not able comment, but anyway
test $some_var; bg_suffix="&"
would cause bg_suffix to be set regardless of the result of test.

Resources