MartyIX
September 8, 2008, 1:02pm
1
I'm preparing for exam and one of exams is to write own test command...
I wonder if in unix is a command which just returns exit code you specify..
I know I can easily write a function like this:
exStatus() {
return $1
}
-> my question is rather theoretical
thank you!
Are you expected to do it as a shell script? If so, why? It seems like a pointless task even as a learning experience.
testing()
{
test "$@"
echo $?
}
MartyIX
September 8, 2008, 5:45pm
3
@cfajohnson : Of course without using test command
Well, pointless maybe but it's not so easy to do I've already done a half but it took me three hours
What do you have so far? What else do you need?
Does it have to be a complete replacement for test?
MartyIX
September 8, 2008, 6:08pm
5
I've got operations: -gt -lt == ( ) -z -n
I'm working on it and I'll post it here tomorrow (It's midnight here) I hope I'll finish the most important things
Well, it doesn't have to be a complete replacement but the more the bette It's a good for exercise...
The standard test doesn't have a '==' operator.
I look forward to seeing it.
Do you have to do it without external commands?
MartyIX
September 8, 2008, 7:57pm
7
> Do you have to do it without external commands?
I'm not sure what you exactly mean. I may use whatever command except test - I got the assignment this way.
> I look forward to seeing it.
I don't think you'll be pleased with great source code
>The standard test doesn't have a '==' operator.
You're right it has just '=' operator.
examples:
./test.sh 1 = 2 -a -z " " -o -n " " 2>/dev/null # I'm sending debugging info to stderr
./test.sh '(' 1 -lt 2 ')' -a '(' -z " " -o -n " " ')' 2>/dev/null
#!/bin/sh
# File: test.sh
verbose=2; # TODO
# for debugging purposes; TODO - delete after finishing
if [ "$1" == "-x" ]; then
set -x;
shift 1
fi
expr2() { # expr without output
result=$( expr "$@" )
return $( expr $result "=" 0 )
}
usage() {
echo "Usage: "; # TODO
}
process() {
num_stack=0;
op_stack=0;
number1=0;
number2=0;
op="";
opened_with_logical_switch=$1;
deepness=$2
shift 2;
num_stack_in() { # number stack
num_stack=$(( $num_stack + 1 ));
echo "Deepness: $deepness; Num_stack: $num_stack; Value: $1" >&2
eval "number${num_stack}=$1"
if expr2 $num_stack ">" 2; then
echo "Error: Stack overflow";
exit 1;
elif expr2 $num_stack "=" 2; then
return 0;
else
return 1
fi
}
op_stack_in() { # operation stack
op_stack=$(( $op_stack + 1 ));
eval "op${op_stack}=\"$1\""
}
op_stack_out() {
eval "op=\$op${op_stack};"
op_stack=$(( $op_stack - 1 ));
}
do_op() {
op_stack_out;
case "$op" in
"="|">"|"<"|"!=") num_stack=0;
num_stack_in $(expr $number1 "$op" $number2);;
"-a") num_stack=0;
echo $number1 $number2 >&2;
if expr2 $number1 "=" 1 && expr2 $number2 "=" 1; then
num_stack_in 1;
else
num_stack_in 0;
fi;;
"-o") num_stack=0;
if expr2 $number1 "=" 1 || expr2 $number2 "=" 1; then
num_stack_in 1;
else
num_stack_in 0;
fi;;
*) echo "Unknown operation: "'`'"$op\""; exit 1;;
esac
}
while expr2 $# ">" "0"; do
switch="$1"
echo 'Deepness: '"$deepness"'; Switch: `'"$switch\"" >&2
shift
case "$switch" in
')') #if expr2 $opened_with_bracket "=" 0; then
# echo "$number1 "$(($# - 1)); # process ")" again
#else
echo "$number1 $#";
#fi
return 0;;
'(') partial_result=$(process 0 $(( $deepness + 1 )) "$@");
switch=$(echo "$partial_result" | cut -d" " -f 1);
remaining_switches=$(echo "$partial_result" | cut -d" " -f 2);
shift $(($# - $remaining_switches));; # shift after ")"
# test if $1 is empty
"-z") if expr2 "dd${1}dd" "=" "dddd"; then
switch=1;
else
switch=0;
fi
shift;;
"-n") if expr2 "dd${1}dd" "=" "dddd"; then
switch=0;
else
switch=1;
fi
shift;;
esac
case "$switch" in
"-a"|"-o") if expr2 $opened_with_logical_switch "=" 1; then
# $# + 1 because we want to process -a or -o again.
# (in this function the switch -a / -o are shifted)
echo "$number1 "$(($# + 1));
return 0;
fi
op_stack_in "$switch";
partial_result=$(process 1 $(( $deepness + 1 )) "$@");
if echo "$partial_result" | grep -q '^[10]\{1\}$'; then
switch=$partial_result;
remaining_switches=0;
else
switch=$(echo "$partial_result" | cut -d" " -f 1);
remaining_switches=$(echo "$partial_result" | cut -d" " -f 2);
fi
if num_stack_in $switch; then
do_op # with "$number1" "$number2"
fi
shift $(( $# - $remaining_switches ));;
[0-9]*) if num_stack_in "$switch"; then
do_op # with "$number1" "$number2"
fi;;
"=") op_stack_in "=";;
"!=") op_stack_in "!=";;
')') op_stack_in ')';;
'(') op_stack_in '(';;
'-gt') op_stack_in '>';;
'-lt') op_stack_in '<';;
*) echo 'Switch `'"$switch\" - not known"; exit 2;;
esac
done;
if expr2 $num_stack "=" 1 && expr2 $number1 "=" "1"; then
echo 1
return 0;
else
echo 0
return 1;
fi
}
# MAIN SCRIPT STARTS HERE
# =========================
if expr2 "$#" "=" "0"; then
usage;
else
process 0 "1" "$@" # verbose version
# process "1" "$@" >/dev/null # silent version
exit $?
fi
THERE MAY BE A LOT OF BUGS ---> IT'S 2AM; AND THERE'RE A LOT Of FEATURES TO ADD...
Enjoy
If you can use external commands, i.e., those that are not built into the shell, like awk, sed, expr, etc., it becomes an exercise in parsing arguments, since external commands can do all the testing for you.
It's not too bad, though you should look at using case statements instead of external commands whenever possible; it will speed up your script. For example:
case $# in
0) usage; exit 1;;
1) ## if 1 arg, check for null string
case $1 in
"") exit 1 ;; ## empty string
*) exit 0 ;;
esac
2) two_args "$@" ;; ## two_args() checks unary operators
3) three_args "$@" ;; ## three_args() checks binary operators
4) four_args "%@" ;;
* ) parse_compound_tests ;; ## etc...
esac
##
MartyIX
September 9, 2008, 3:23am
9
If you can use external commands, i.e., those that are not built into the shell, like awk, sed, expr, etc., it becomes an exercise in parsing arguments, since external commands can do all the testing for you.
Well yes, in fact without external commands I would be at a loss - it would be pretty much work
... and I thought right :-)))
Sure you're right -> one example of bad code for all:
if expr2 $num_stack ">" 2; then
echo "Error: Stack overflow";
exit 1;
elif expr2 $num_stack "=" 2; then
return 0;
else
return 1
fi
I'll try to adjust the code..
MartyIX
September 9, 2008, 5:47am
10
New commands: -nt, -ot, -ef
I've adjusted the code and it seems to speed up at least twice:
#!/bin/sh
# File: test.sh
# Author: Martin Vseticka, 2008
verbose=0;
debug_info() {
case $verbose in
[0]) return 0;;
[1]) echo "$@" >&2
esac
}
expr2() { # expr without output
result=$( expr "$@" )
return $( expr $result "=" 0 )
}
usage() {
echo "Usage: "; # TODO
}
process() {
num_stack=0;
op_stack=0;
number1=0;
number2=0;
op="";
opened_with_logical_switch=$1;
deepness=$2
shift 2;
num_stack_in() { # number stack
num_stack=$(( $num_stack + 1 ));
debug_info "Deepness: $deepness; Num_stack: $num_stack; Value: $1"
eval "number${num_stack}=$1"
case $num_stack in
[0]|[1]) return 1;;
[2]) return 0;;
*) echo "Error: Stack overflow";
exit 1;;
esac
}
op_stack_in() { # operation stack
op_stack=$(( $op_stack + 1 ));
eval "op${op_stack}=\"$1\""
}
op_stack_out() {
eval "op=\$op${op_stack};"
op_stack=$(( $op_stack - 1 ));
}
do_op() {
op_stack_out;
case "$op" in
"="|">"|"<"|"!=") num_stack=0;
num_stack_in $(expr $number1 "$op" $number2);;
"-a"|"-o") num_stack=0;
case "$number1 $number2" in
'1 1') num_stack_in 1;;
*) num_stack_in 0;;
esac;;
'-ef') num_stack=0;
do_op_equal=$(ls -i "$number1" "$number2" | while read inode_num file; do
echo $inode_num
done | tr "\n" " " | awk '{ if ($1 == $2){ print "1"; }else{ print "0"; } }' );
if expr2 "$do_op_equal" "=" "1" &&
expr2 "$(stat -c %D "$number1")" "=" "$(stat -c %D "$number2")" &&
expr2 "$(stat -c %D "$number1")" "=" "$(stat -c %D "$number2")"; then
num_stack_in 1;
else
num_stack_in 0;
fi;;
'-nt'|'-ot') case "$op" in
'-nt') do_op_res=$(find "$number1" -newer "$number2" -print);;
'-ot') do_op_res=$(find "$number2" -newer "$number1" -print);;
esac
# any do_op_res ~ true; otherwise false
case "dd${do_op_res}dd" in
'dddd') switch=0;;
*) switch=1;;
esac
num_stack=0;
num_stack_in $switch;;
*) echo "Unknown operation: "'`'"$op\""; exit 1;;
esac
}
while true; do
case $# in
[0]) break;; # stop the main loop
esac
switch="$1"
debug_info 'Deepness: '"$deepness"'; Switch: `'"$switch\""
shift
case "$switch" in
')') echo "$number1 $#";
return 0;;
'(') partial_result=$(process 0 $(( $deepness + 1 )) "$@");
switch=$(echo "$partial_result" | cut -d" " -f 1);
remaining_switches=$(echo "$partial_result" | cut -d" " -f 2);
shift $(($# - $remaining_switches));; # shift after ")"
# test if $1 is empty
"-z") case "dd${1}dd" in
'dddd') switch=0;;
*) switch=1;;
esac
shift;;
# test if $1 is non-zero length string
"-n") case "dd${1}dd" in
'dddd') switch=0;;
*) switch=1;;
esac
shift;;
esac
case "$switch" in
"-a"|"-o") case $opened_with_logical_switch in
[1]) # $# + 1 because we want to process -a or -o again.
# (in this function the switch -a / -o are shifted)
echo "$number1 "$(($# + 1));
return 0;;
esac
op_stack_in "$switch";
partial_result=$(process 1 $(( $deepness + 1 )) "$@");
if echo "$partial_result" | grep -q '^[10]\{1\}$'; then
switch=$partial_result;
remaining_switches=0;
else
switch=$(echo "$partial_result" | cut -d" " -f 1);
remaining_switches=$(echo "$partial_result" | cut -d" " -f 2);
fi
if num_stack_in $switch; then
do_op # with "$number1" "$number2"
fi
shift $(( $# - $remaining_switches ));;
[0-9]*) if num_stack_in "$switch"; then
do_op # with "$number1" "$number2"
fi;;
"=") op_stack_in "=";;
"!=") op_stack_in "!=";;
')') op_stack_in ')';;
'(') op_stack_in '(';;
'-gt') op_stack_in '>';;
'-lt') op_stack_in '<';;
'-ef'|'-ot'|'-nt') op_stack_in "$switch";;
*) if num_stack_in "$switch"; then
do_op # with "$number1" "$number2"
fi;;
#*) echo 'Switch `'"$switch\" - not known"; exit 2;;
esac
done;
case "$num_stack $number1" in
'1 1') echo 1
return 0;;
*) echo 0
return 1;;
esac
}
# MAIN SCRIPT STARTS HERE
# =========================
# for debugging purposes; TODO - delete after finishing
if [ "$1" == "-x" ]; then
set -x;
shift 1
fi
# for debugging purposes;
if [ "$1" == "-v" ]; then
verbose=1
shift 1
fi
case "$#" in
0) usage;;
*) process 0 "1" "$@" # verbose version
# process "1" "$@" >/dev/null # silent version
exit $?
esac