redirection using script parameters in bash - bash

edit: thank you I think that syntax wise the script is now running ok.
My script takes 4 parameters:
log_file_name
program_name
input_file_name
output_file_name
I want to redirect the input file into the program (in c) and then redirect the output into the log file.
then check differences with the desired given output file (parameter 4).
what is the most elegant way to do this?
I tried the following code which didn't work-
#!/bin/bash
$2.c < $3 > $1
var = `diff $1 $4`
if [[ var=="" ]]
then echo "Out files match"
exit 0
fi
echo "Out files mismatch"
exit 1
I have another script which compiles the .c file beforehand
#!/bin/bash
gcc -Wall -o $2 $2.c &> $1
var=`grep -e warnings -e errors $1`
if [[ $var == "" ]]
then echo "Compile succeeded"
exit 0
fi
echo "Compile failed"
exit 1

#!/bin/bash
var=`diff $1 $4`
if [[ $var == "" ]];then
echo "Out files match"
exit 0
else
echo "Out files mismatch"
echo " < : Actual output "
echo " > : Excepted output "
diff $1 $4 | grep -v "^---" | grep -v "^[0-9c0-9]"
exit 1
fi
Try above code to difference between output log and actual output

Related

raise error if files output some error (without raising shell error)

I want to do raise an error if an executable outputs something specific...(like "error"):
echo "abcdef" | [[ "$(grep "f")" != "" ]] && echo "error"
"error"
echo "abcdef" | [[ "$(grep "g")" != "" ]] && echo "error"
""
This work but when applying it to an exe it fails
test_if_exe_fails(){
$1 | [[ "$(grep "$2")" != "" ]] && echo "error: $2" && exit 1
}
when running myApp.exe | test_if_exe_fails "myerror message" it exits
line xxx: myerror : command not found
so I tried using this method but still doesn't work
let's say the output of my exe start with "BLA BLA FOO", if I do:
test_if_exe_fails "$(myApp.exe)" "myerror message"
it gives :
line xxx: BLA : command not found
more examples:
1/
test_if_exe_fails "$(echo 'hello')" "hola"
line xx: hello: command not found
2/
test_if_exe_fails "$(echo 'hello')" "hello"
line xx: hello: command not found
The purpose of [[ ... ]] is to generate an exit code that if, &&, or || can test. grep already does that. You don't care about grep's output, and you can suppress it with the -q option.
echo "abcdef" | grep -q "f" && echo "error"
Note that your function is wrapping the pipeline, but you have removed the echo command. You need to put that back. (Actually, you should use printf instead of echo for portability and predictability. And finally, error messages should be written to standard error, not standard output.
test_if_exe_fails(){
print '%s' "$1" | grep -q "$2" && echo "error: $2" >&2 && exit 1
}
test_if_exe_fails "$(myApp.exe)" "myerror message"
One last improvement: rather than capture the output of myApp.exe, just pipe it directly to the function:
test_if_exe_fails(){
grep -q "$1" && echo "error: $1" >&2 && exit 1
}
myApp.exe | test_if_exe_fails "myerror message"
So your function
test_if_exe_fails(){
$1 | [[ "$(grep "$2")" != "" ]] && echo "error: $2" && exit 1
}
Says "Run the program $1 and grep the output for $2"
That means that when you call this function you should supply the NAME of the program you want to run.
But that is silly. In the examples with echo you want arguments, so you can't do that like that. There are a number of ways to solve this, but they all involve the function NOT RUNNING THE PROGRAM AGAIN!

Setting exit codes in Unix shell scripts

I have test.sh which has multiple return condition and test1.sh just echo statement.When i run test2.sh my logic should run test.sh process the file i.e "File successfully" and call test1.sh script .It should not run the test1.sh script when other else condition was executed.i.e "File not successfully", "Input file doesn't exists in directory"
The problem i am facing is when it is executing other condition like "File not successfully", "Input file doesn't exists in directory" it is not retuning "1" as specified exit code but in turn returning 0 i.e from the OS means job was successful. So i am getting "0" from test.sh for all the different condition so test1 .sh is getting called irrespective if the file processed failed etc.Pls advice with the return code
test.sh
FILES=/export/home/input.txt
cat $FILES | nawk -F '|' '{print $1 "|" $2 "|" }' $f > output.unl
if [[ -f $FILES ]];
then if [[ $? -eq 0 ]]; then
echo "File successfully"
else
echo "File not successfully"
exit 1
fi
else
echo "Input file doesn't exists in directory" exit 1 fi
========================================================================
test1.sh
echo "Input file exists in directory"
test2.sh
echo "In test2 script"
./test.sh
echo "return code" $?
if [[ $? -eq 0 ]]; then
echo "inside"
./test1.sh
fi
You're overwriting $? when you use it in echo - after that, it contains the exit code of echo itself. Store it in a variable to avoid this.
echo "In test2 script"
./test.sh
testresult=$?
echo "return code" $testresult
if [[ $testresult -eq 0 ]]; then
echo "inside"
./test1.sh
fi
Edited to add: it's hard to tell what you want from test.sh as the code you pasted is incomplete and doesn't even run. It looks like you meant the cat to be inside the if, because otherwise it errors when the input file is missing, and your $? test does nothing. So I rearranged it like this:
FILES=input.txt
if [[ -f $FILES ]]; then
cat $FILES | awk -F '|' '/bad/{ exit 1 }'
if [[ $? -eq 0 ]]; then
echo "File processed successfully"
else
echo "File processing failed"
exit 1
fi
else
echo "Input file doesn't exist in directory"
exit 1
fi
I've changed the awk script to demonstrate the conditions all work: now, if I put the word bad in input.txt you'll see the "File processing failed" message, otherwise you see success; remove the file and you'll see the input file doesn't exist message.

What causes the exit status to change for the same command even when the output is identical?

In the below code, I am trying to check if the command within the if condition completed successfully and that the data was pushed into the target file temp.txt.
Consider:
#!/usr/bin/ksh
A=4
B=1
$(tail -n $(( $A - $B )) sample.txt > temp.txt)
echo "1. Exit status:"$?
if [[ $( tail -n $(( $A - $B )) sample.txt > temp.txt ) ]]; then
echo "2. Exit status:"$?
echo "Command completed successfully"
else
echo "3. Exit status:"$?
echo "Command was unsuccessfully"
fi
Output:
$ sh sample.sh
1. Exit status:0
3. Exit status:1
Now I can't get why the exit status changes above.. when the output of both the instances of the tail commands are identical. Where am I going wrong here..?
In the first case, you're getting the exit status of a call to the tail command (the subshell you spawned with $() preserves the last exit status)
In the second case, you're getting the exit status of a call to the [[ ]] Bash built-in. But this is actually testing the output of your tail command, which is a completely different operation. And since that output is empty, the test fails.
Consider :
$ [[ "" ]] # Testing an empty string
$ echo $? # exit status 1, because empty strings are considered equivalent to FALSE
1
$ echo # Empty output
$ echo $? # exit status 0, the echo command executed without errors
0
$ [[ $(echo) ]] # Testing the output of the echo command
$ echo $? # exit status 1, just like the first example.
1
$ echo foo
foo
$ echo $? # No errors here either
0
$ [[ $(echo foo) ]]
$ echo $? # Exit status 0, but this is **NOT** the exit status of the echo command.
0

Make Bash script exit and print error message if users invoke the script incorrectly

Script needed was
#!/bin/bash
# Check if there are two arguments
if [ $# -eq 2 ]; then
# Check if the input file actually exists.
if ! [[ -f "$1" ]]; then
echo "The input file $1 does not exist."
exit 1
fi
else
echo "Usage: $0 [inputfile] [outputfile]"
exit 1
fi
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "$1" > "$2"
Edit, the script has changed to
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
if [ ! -f "$1" ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
invoking the script with no parameters gives no erros and sits blank
Usage:
./Scriptname inputfile > outputfile
I have bit of code
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
This code pulls lines that have a single word on them and pumps the output to a new file, so for example
This is a multi word line
this
the above line is not
now
once again wrong
The output would be
This
now
The code works, users invoke the code using ./scriptname file > newfile
However, I am trying to expand the code to give users an error message if they invoke the script incorrectly.
For the error messange, I'm thinking of echoing something back like scriptname file_to_process > output_file.
I did try
if [incorrectly invoted unsure what to type]
echo $usage
exit 1
Usage="usage [inputfile] [>] [outputfile]
However I have had little luck. The code runs but does nothing if I invoke with just the script name. Also, if I invoke the script with just the scriptname and the input file, it will output the results instead of exiting with the error message.
Other ones I have tried are
if [ ! -n $1 ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
Given replies I have received so far, my code now is
#!/bin/bash
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
if [ ! -f "$1" ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
When invoking the script without an input file the script does nothing and has to be aborted with ctrl+c, still trying to get the echo of the invoke message.
When you are invoking the script like ./scriptname file > newfile, the shell interprets file as the only argument to ./scriptname. This is because > is the standard output redirection operator.
I would like to propose 2 possible alternatives:
Alternative 1:
Maybe you're can try passing it as 1 argument like this?
./scriptname 'file > newfile'
In that case one way to check the format would be
#!/bin/bash
# Check if the format is correct
if [[ $1 =~ (.+)' > '(.+) ]]; then
# Check if the input file actually exists.
if ! [[ -f "${BASH_REMATCH[1]}" ]]; then
echo "The input file ${BASH_REMATCH[1]} does not exist!"
exit 1
fi
else
echo "Usage: $0 \"[inputfile] [>] [outputfile]\""
exit 1
fi
# Redirect standard output to the output file
exec > "${BASH_REMATCH[2]}"
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "${BASH_REMATCH[1]}"
Note: If you are checking whether the arguments are valid or not, it's generally better to run commands only after the checking is done.
Alternative 2:
Passing 2 arguments like
./scriptname file newfile
The script looks like this
#!/bin/bash
# Check if there are two arguments
if [ $# -eq 2 ]; then
# Check if the input file actually exists.
if ! [[ -f "$1" ]]; then
echo "The input file $1 does not exist."
exit 1
fi
else
echo "Usage: $0 [inputfile] [outputfile]"
exit 1
fi
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "$1" > "$2"
I'd use parameter expansion for this:
inputfile=${1:?Usage: $(basename $0) inputfile > outputfile}
If the script is called without arguments (i.e. $1 is unset) the ${var:?error message} expansion causes the shell to display an error with the given message and exit. Otherwise the first argument is assigned to $inputfile.
Try to add double quotes around $1 and use -f to check for exists and is normal file:
if [ ! -f "$1" ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
Also you can check for the param count with $# and cat an usage message:
if [ ! $# -eq 1 ]; then
cat << EOF
Usage:
$0 'input_file' > output_file
EOF
exit 1
fi

Logical Error in Shell script.Please help (UNIX)

Following is a source code which takes in only 'files',lists the file permissions of a file and prints the output by replacing
r=READ,w-WRITE,x-EXECUTABLE.
It should also echo "User".But the My problem here is that I have replaced '-' by User but then if the file has a permission of r--x,it also prints "User" # that point.I know its not a correct way to go about it.Can anyone suggest me a better way of echoing "User".
I have also tried printing it before the loop but then it won't serve my purpose, as My program only works withe file permissions of a FILE and not any block/socket/pipe/directory/etc.
#!/bin/bash
if [ $# -lt 1 ];then
echo "USAGE: $0 file-name"
exit 1
fi
ls -l $1 | cut -c1-4 | tr "\012" "." > fp
i=1
while(($i <= 4))
do
p=`cat fp | cut -c$i`
case $p in
[dbsplc] | t) echo "not a file";
exit 1;;
-) echo "User";;
r) echo "READ";;
w) echo "WRITE";;
x) echo "EXECUTE";;
esac
((++i))
done
exit 0
Too complicated. You don't have to rely on ls at all:
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "USAGE: $(basename "$0") filename ..."
exit 1
fi
exit_status=0
for file in "$#"; do
if [[ ! -f "$file" ]]; then
echo "not a file: $file" >&2
exit_status=$(( exit_status + 1 ))
continue
fi
echo "$file:"
echo "User"
[[ -r "$file" ]] && echo "READ"
[[ -w "$file" ]] && echo "WRITE"
[[ -x "$file" ]] && echo "EXECUTE"
done
exit $exit_status
I'd just use stat -c %a and process that instead.
an exemple using awk (easily adaptable to your program)
ll |awk '{
rights=substr($1, 2, 3);
sub(/r/, "READ ", rights);
sub(/w/, "WRITE ", rights);
sub(/x/, "EXECUTE ", rights);
print rights $3
}'
Explanations :
rights=substr($1, 2, 3);
$1 contains rights of your program and we only takes the 3 first rights (user one)
sub(/r/, "READ ", rights);
Substiture "r" with READ in rights (and so on).
print rights $3
Print rights (substituated) and $3 that contains the user name.
This served my purpose,I separated the first condition into a different case-statement.:
#!/bin/bash
if [ $# -lt 1 ];then
echo "USAGE: $0 file-name"
exit 1
fi
ls -l $1 | cut -c1-4 | tr "\012" "." > fp
i=1
while(($i == 1))
do
p=`cat fp | cut -c$i`
case $p in
[dbsplc] | t) echo "not a file";
exit 1;;
esac
echo "User"
((++i))
done
while(($i <= 4))
do
p=`cat fp | cut -c$i`
case $p in
r) echo "READ";;
w) echo "WRITE";;
x) echo "EXECUTE";;
esac
((++i))
done
exit 0

Resources