I wrote the following code :
echo "Choose between the following options:"
echo "1 - Create a new file"
echo "2 - Write in an existing file"
echo "3 - Change the path of a file"
echo "4 - Display a file"
echo "5 - Exit"
read number
while [ $number -ne 1 -o $number -ne 2 -o $number -ne 3 -o $number -ne 4 -o $number -ne 5 ]
do
echo "Enter a number between 1 and 5"
read number
done
if [ $number -eq 1 ]; then
echo "Enter a folder name"
read name
while [ -e $name ]
do
echo "The file name already exists enter a new name:"
read name
done
touch $name
fi
if [ $number -eq 2 ]; then
echo "Enter the folder name you want to edit :"
read name
while [ ! -f $name ]
do
echo "The file you are looking does not exist. Enter another file name :"
read name
done
echo "Enter what you want to put in the file :"
read input
echo $input >> $name
fi
if [ $number -eq 3 ]; then
echo "Enter the folder name :"
read name
while [ ! -f $name ]
do
echo "The file you are looking does not exist. Enter another file name :"
read name
done
if [ $number -eq 4 ]; then
echo "Enter the folder name you want to see :"
read name
while [ ! -f $name ]
do
echo "The file you are looking does not exist. Enter another file name :"
read name
done
cat $name
fi
if [ $number -eq 5 ]; then
exit 0
fi
The code works just fine, but on the first condition :
while [ $number -ne 1 -o $number -ne 2 -o $number -ne 3 -o $number -ne 4 -o $number -ne 5 ]
I would like it to work to whatever number or string I put.
For example, if I put hello the program will crash.
Can someone tell me what should my first condition be?
Thank you for your help. And forgive me if my question is not in the rules of the forum (I just subscribed).
Your script says:
while [ $number -ne 1 -o $number -ne 2 -o $number -ne 3 -o $number -ne 4 -o $number -ne 5 ]
So, if $number is 1, it's not equal to 2. And if it's 2, it's not equal to 1. This will always evaluate as true, so you'll never exit the loop.
A variety of options exist that will be compatible with a numeric value and still gracefully handle non-numeric input. The following uses a basic regular expression to determine whether input is a digit from 1 to 5:
while read number && ! expr "$number" : '[1-5]$' >/dev/null; do
echo "Try again" >&2
done
You would probably be better off, though, using a case statement:
while read number; do
case "$number" in
1) function_1 ;;
2) function_2 ;;
... etc
*) echo "Invalid input, please try again." >&2; continue ;;
esac
break
done
The case statement is a more graceful way of expressing what might otherwise be achieved using if..elif..elif..fi:
while read number && ! expr "$number" : '[1-5]$' >/dev/null; do
echo "Try again" >&2
done
if [ "$number" = 1 ]; then
: do_something
elif [ "$number" = 2 ]; then
: do_something
elif [ "$number" = 3 ]; then
: do_something
elif [ "$number" = 4 ]; then
: do_something
elif [ "$number" = 5 ]; then
: do_something
else
echo "What am I doing?" >&2
fi
While this construct technically works, it's inelegant and harder to read. Much better to put your functionality into functions which get called from a case statement.
Note that it's always a good idea to quote your variables within a script like this. Do you know what happens with unquoted variables? If not, then quote your variables. :)
I would strongly suggest structuring your code with a case statement:
case $number in
1) code_for_1;;
2) code_for_2;;
...
*) echo "Invalid number" >&2;;
esac
If you wish to repeat (I would actually recommend taking the number as a command line argument instead of reading from stdin and aborting on an error), you could simply do:
while read number; do
case $number in
...
*) echo 'Invalid number' >&2; continue;;
esac
break
done
And write code_for_{1,2,3,4,5} as functions to factor out the logic. For example:
create_file() {
echo "Enter a file name"
while read name; do
if test -e "$name"; then
echo "The file $name already exists enter a new name" >&2
continue
fi
touch "$name"
break
done
}
while read number; do
case $number in
1) create_file;;
...
*) echo 'Invalid number' >&2; continue;;
esac
break
done
Related
Have two files:
file1 is having the key words - INFO ERROR
file2 is having the list of log files path - path1 path2
I need to exit out of the script if any of the condition in any of the loops failed.
Here is the Code:
#!/bin/bash
RC=0
while read line
do
echo "grepping from the file $line
if [ -f $line ]; then
while read key
do
echo "searching $key from the file $line
if [ condition ]; then
RC=0;
else
RC=1;
break;
fi
done < /apps/file1
else
RC=1;
break;
fi
done < apps/file2
exit $RC
Thank you!
The ansewer to your question is using break 2:
while true; do
sleep 1
echo "outer loop"
while true; do
echo "inner loop"
break 2
done
done
I never use this, it is terrible when you want to understand or modify the code.
Already better is using a boolean
found_master=
while [ -n "${found_master}" ]; do
sleep 1
echo "outer loop"
while true; do
echo "inner loop"
found_master=true
break
done
done
When you do not need the variable found_master it is an ugly additional variable.
You can use a function
inner_loop() {
local i=0;
while ((i++ < 5)); do
((random=$RANDOM%5))
echo "Inner $i: ${random}"
if [ ${random} -eq 0 ]; then
echo "Returning 0"
return 0
fi
done;
return 1;
}
j=0
while ((j++ < 5 )); do
echo "Out loop $j"
inner_loop
if [ $? -eq 0 ]; then
echo "inner look broken"
break
fi
done
But your original problem can be handles without two while loops.
You can use grep -E "INFO|ERROR" file2 or combining the keywords. When the keywords are on different lines in file1, you can use grep -f file1 file2.
Replace condition with $(grep -c ${key} ${line}) -gt 0 like this:
echo "searching $key from the file $line
if [ $(grep -c ${key} ${line}) -eq 0 ]; then
It will count the each key-word in your log-file. If count=0 (pattern didn't found), running then. If found at least 1 key, running else, RC=1 and exit from loop.
And be sure, that your key-words can't be substrings of the longest words, or you will get an error.
Example:
[sahaquiel#sahaquiel-PC Stackoverflow]$ cat file
correctstringERROR and more useless text
ERROR thats really error string
[sahaquiel#sahaquiel-PC Stackoverflow]$ grep -c ERROR file
2
If you wish to avoid count 2 (because counting first string, obliviously, bad way), you should also add two keys for grep:
[sahaquiel#sahaquiel-PC Stackoverflow]$ grep -cow ERROR file
1
Now you have counted only the words equal to your key, not substrings in any useful strings.
I am writing a script where I need to list files without displaying them. The below script list the files while executing which I don't want to do. Just want to check if there are files in directory then execute "executing case 2".
ls -lrt /a/b/c/
if [ $? != 0 ]
then
echo "executing case 2"
else
echo "date +%D' '%TNo files found to process" >> $LOG
Testing the return code of ls won't do you a lot of good, because it'll return zero in both cases where it could list the directory.
You could do so with grep though.
e.g.:
ls | grep .
echo $?
This will be 'true' if grep matched anything (files were present). And false if not.
So in your example:
ls | grep .
if [ $? -eq 0 ]
then
echo "Directory has contents"
else
echo "directory is empty"
fi
Although be cautious with doing this sort of thing - it looks like you're in danger of a busy-wait test, which can make sysadmins unhappy.
If you don't need to see the output of ls, you could just make it a condition:
[ "$(ls -lrt a/b/c)" ] && echo "Not Empty" || echo "Empty"
Or better yet
[ "$(ls -A a/b/c)" ] && echo "Not Empty" || echo "Empty"
Since you don't care about long output (l) or display order (rt).
In a script, you could use this in an if statement:
#!/bin/sh
if [ "$(ls -A a/b/c)" ]; then
echo "Not empty"
else
echo "Empty"
fi
I have the below script that is expected to work when the user invokes sh <scriptName> <propertyfile> It does work when I provide this at the dollar prompt. However, I am having two issues with the script.
If I provide just one argument, ie if I do - sh <scriptName>, I see the below error -
my-llt-utvsg$ sh temp.sh
Usage temp.sh
When I do -help, I see the below error -
my-llt-utvsg$ sh tmp.sh -help
-help does not exist
What am I doing wrong? Can someone please advise? I am a software developer that very rarely needs to do shell scripting, so please go easy on me ;)
#!/bin/bash
FILE="system.properties"
FILE=$1
if [ ! -f $FILE ];
then
echo "$FILE does not exist"
exit
fi
usage ()
{
echo "Usage $0 $FILE"
exit
}
if [ "$#" -ne 1 ]
then
usage
fi
if [ "$1" = "-help" ] ; then
echo ""
echo '############ HELP PROPERTIES ############ '
echo ""
echo 'Blah.'
exit
The reason your
if [ "$1" = "-help" ] ; then
check is not working is that it only checks $1 or the first argument.
Try instead:
for var in "$#"
do
if [ "$var" = "-help" ] ; then
echo ""
echo '############ HELP PROPERTIES ############ '
echo ""
echo 'Blah.'
fi
done
Which will loop over each argument and so will run if any of them are -help.
Try this as well:
#!/bin/bash
FILES=()
function show_help_info_and_exit {
echo ""
echo '############ HELP PROPERTIES ############ '
echo ""
echo 'Blah.'
exit
}
function show_usage_and_exit {
echo "Usage: $0 file"
exit
}
for __; do
if [[ $__ == -help ]]; then
show_help_info_and_exit
elif [[ -f $__ ]]; then
FILES+=("$__")
else
echo "Invalid argument or file does not exist: $__"
show_usage_and_exit
fi
done
if [[ ${#FILES[#]} -ne 1 ]]; then
echo "Invalid number of file arguments."
show_usage_and_exit
fi
echo "$FILES"
I have been trying to execute the following UNIX shell script which is not working.
I am running it by KornShell (ksh).
echo $?;
if [ $? -ne 0 ]
then
failed $LINENO-2 $5 $6
fi
failed()
{
echo "$0 failed at line number $1";
echo "moving $2 to failed folder"
}
This is giving an error saying Syntax error:then unexpected.. Basically I have to check for the last executed ksh script's highest/last statement's return code and if it is not equal to zero I have to call function failed with the given parameters. I tried putting semicolon before then but that also did not work.
Can you please help?
Edit1: Based on the inputs I changed code. Still the same problem exists.
ksh ../prescript/Pre_process $1 $2 $3
rc=$?;
if [[ $rc -ne 0 ]];then
echo "failed";
exit 1;
Edit2:
It is working for the then part by using double squared brackets. I feel I used code of bash script for ksh. I am facing problem in function call of failed. Please let me know appropriate way of function call in ksh for this example
This looks like bash rather than ksh
failed() {
echo "$0 failed at line number $1";
echo "moving $2 to failed folder"
}
if [[ $? -ne 0 ]]
then
failed $LINENO-2 $5 $6
fi
You need to be careful. The first operation on $? will usually clear it so that your if won't work anyway.
You would be better off using:
rc=$?
echo $rc
if [ $rc -ne 0 ]
:
Other than that, it works fine for me:
$ grep 1 /dev/null
$ if [ $? -ne 0 ]
> then
> echo xx
> fi
xx
$ grep 1 /dev/null
$ echo $?;
1
$ if [ $? -ne 0 ]
> then
> echo yy
> fi
$ _
Note the lack of output in the last one. That's because the echo has sucked up the return value and overwritten it (since the echo was successful).
As an aside, you should let us know which UNIX and which ksh you're actually using. My working version is ksh93 under Ubuntu. Your mileage may vary if you're using a lesser version.
It looks like, from your update, your only problem now is the function call. That's most likely because you're defining it after using it. The script:
grep 1 /dev/null
rc=$?
if [ $rc -ne 0 ]
then
failed $rc
fi
failed()
{
echo Return code was $1
}
produces:
qq.ksh[6]: failed: not found
while:
failed()
{
echo Return code was $1
}
grep 1 /dev/null
rc=$?
if [ $rc -ne 0 ]
then
failed $rc
fi
produces
Return code was 1
you are missing semicolons at the end of the lines:
if [ $? -ne 0]; then
# …
This question already has answers here:
How do I test if a variable is a number in Bash?
(40 answers)
Closed 1 year ago.
How do I check to see if a variable is a number, or contains a number, in UNIX shell?
if echo $var | egrep -q '^[0-9]+$'; then
# $var is a number
else
# $var is not a number
fi
Shell variables have no type, so the simplest way is to use the return type test command:
if [ $var -eq $var 2> /dev/null ]; then ...
(Or else parse it with a regexp)
No forks, no pipes. Pure POSIX shell:
case $var in
(*[!0-9]*|'') echo not a number;;
(*) echo a number;;
esac
(Assumes number := a string of digits). If you want to allow signed numbers with a single leading - or + as well, strip the optional sign like this:
case ${var#[-+]} in
(*[!0-9]*|'') echo not a number;;
(*) echo a number;;
esac
In either ksh93 or bash with the extglob option enabled:
if [[ $var == +([0-9]) ]]; then ...
Here's a version using only the features available in a bare-bones shell (ie it'd work in sh), and with one less process than using grep:
if expr "$var" : '[0-9][0-9]*$'>/dev/null; then
echo yes
else
echo no
fi
This checks that the $var represents only an integer; adjust the regexp to taste, and note that the expr regexp argument is implicitly anchored at the beginning.
This can be checked using regular expression.
###
echo $var|egrep '^[0-9]+$'
if [ $? -eq 0 ]; then
echo "$var is a number"
else
echo "$var is not a number"
fi
I'm kind of newbee on shell programming so I try to find out most easy and readable
It will just check the var is greater or same as 0
I think it's nice way to choose parameters... may be not what ever... :
if [ $var -ge 0 2>/dev/null ] ; then ...
INTEGER
if echo "$var" | egrep -q '^\-?[0-9]+$'; then
echo "$var is an integer"
else
echo "$var is not an integer"
fi
tests (with var=2 etc.):
2 is an integer
-2 is an integer
2.5 is not an integer
2b is not an integer
NUMBER
if echo "$var" | egrep -q '^\-?[0-9]*\.?[0-9]+$'; then
echo "$var is a number"
else
echo "$var is not a number"
fi
tests (with var=2 etc.):
2 is a number
-2 is a number
-2.6 is a number
-2.c6 is not a number
2. is not a number
2.0 is a number
if echo $var | egrep -q '^[0-9]+$'
Actually this does not work if var is multiline.
ie
var="123
qwer"
Especially if var comes from a file :
var=`cat var.txt`
This is the simplest :
if [ "$var" -eq "$var" ] 2> /dev/null
then echo yes
else echo no
fi
Here is the test without any regular expressions (tcsh code):
Create a file checknumber:
#! /usr/bin/env tcsh
if ( "$*" == "0" ) then
exit 0 # number
else
((echo "$*" | bc) > /tmp/tmp.txt) >& /dev/null
set tmp = `cat /tmp/tmp.txt`
rm -f /tmp/tmp/txt
if ( "$tmp" == "" || $tmp == 0 ) then
exit 1 # not a number
else
exit 0 # number
endif
endif
and run
chmod +x checknumber
Use
checknumber -3.45
and you'll got the result as errorlevel ($?).
You can optimise it easily.
( test ! -z "$num" && test "$num" -eq "$num" 2> /dev/null ) && {
# $num is a number
}
You can do that with simple test command.
$ test ab -eq 1 >/dev/null 2>&1
$ echo $?
2
$ test 21 -eq 1 >/dev/null 2>&1
$ echo $?
1
$ test 1 -eq 1 >/dev/null 2>&1
$ echo $?
0
So if the exit status is either 0 or 1 then it is a integer , but if the exis status is 2 then it is not a number.
a=123
if [ `echo $a | tr -d [:digit:] | wc -w` -eq 0 ]
then
echo numeric
else
echo ng
fi
numeric
a=12s3
if [ `echo $a | tr -d [:digit:] | wc -w` -eq 0 ]
then
echo numeric
else
echo ng
fi
ng
Taking the value from Command line and showing THE INPUT IS DECIMAL/NON-DECIMAL and NUMBER or not:
NUMBER=$1
IsDecimal=`echo "$NUMBER" | grep "\."`
if [ -n "$IsDecimal" ]
then
echo "$NUMBER is Decimal"
var1=`echo "$NUMBER" | cut -d"." -f1`
var2=`echo "$NUMBER" | cut -d"." -f2`
Digit1=`echo "$var1" | egrep '^-[0-9]+$'`
Digit2=`echo "$var1" | egrep '^[0-9]+$'`
Digit3=`echo "$var2" | egrep '^[0-9]+$'`
if [ -n "$Digit1" ] && [ -n "$Digit3" ]
then
echo "$NUMBER is a number"
elif [ -n "$Digit2" ] && [ -n "$Digit3" ]
then
echo "$NUMBER is a number"
else
echo "$NUMBER is not a number"
fi
else
echo "$NUMBER is not Decimal"
Digit1=`echo "$NUMBER" | egrep '^-[0-9]+$'`
Digit2=`echo "$NUMBER" | egrep '^[0-9]+$'`
if [ -n "$Digit1" ] || [ -n "$Digit2" ]; then
echo "$NUMBER is a number"
else
echo "$NUMBER is not a number"
fi
fi