I have small shell script which is used to test the condition whether used is logged in or not using if condition.
#!/bin/bash
if [ $# -ne 1 ]
then
echo "Argument required"
echo "Usage: on user"
else
user="$1"
if who | grep ^"$user " > /dev/null
then
echo "$user is logged in"
else
echo "$user is not logged in"
fi
fi
This would be working correctly.
Now here is my question: if i try this if condition like below method means shell terminal through the error.
if [ who | grep ^"$user " > /dev/null ]
> Error ./user.sh: line 10: [: missing `]'
grep: ]: No such file or directory
could you please some tell what is the difference between these two test condition?
In simple words, the [ or test tests the results of expression, not command list.
Here is the syntax of test and if:
test expr
[ expr ]
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
Then in if construct, it can be followed by a commmand list.
So in if who | grep ^"$user " > /dev/null, it executes the command who | grep ^"$user " > /dev/null, then test the return status of the command.
But in if [ who | grep ^"$user " > /dev/null ], it first executes the command [ who | grep ^"$user " > /dev/null ], but because of the |(pipeline), the commmand will be parsed as two parts:
1st: [ who, which will complain: "line 10: [: missing `]'"
2nd: grep ^"$user " > /dev/null ] which will complain: "grep: ]: No such file or directory".
As for the format of the expression of the [ or test:
refer to The classic test command. And the main four expressoins are:
file tests, string tests, arithmetic tests, misc tests.
Use a function.
#!/bin/bash
logged_in () { who | grep ^"$1 " > /dev/null; }
if [ $# -ne 1 ]
then
echo "Argument required"
echo "Usage: on user"
else
user="$1"
if logged_in "$user"
then
echo "$user is logged in"
else
echo "$user is not logged in"
fi
fi
Related
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!
Here is my code
bash-3.2$ cat nexus.ksh
#!/usr/bin/ksh
LSEARCH=$(ldapsearch -p 389 -x -D "cn=xx" -w xxxx -b "ou=access,ou=nexus,ou=applications,o=xxx,c=xx" cn=$1 | grep -i uid=$2)
echo ldap search output is "${LSEARCH}"
if [ "$LSEARCH" -eq " " ]; then
echo " User is not present in ACL group...Adding User to ACL group"
else
echo "User is present in ACL Group"
fi
output:
bash-3.2$ ./nexus.ksh acl.LINK_ODI_1 xxxx
ldap search output is uniquemember: uid=xxx,ou=people,ou=access,ou=nexus,ou=applications,o=xxxx,c=
./nexus.ksh[4]: uniquemember: uid=xxx,ou=people,ou=access,ou=nexus,ou=applications,o=xxx,c=: bad number
User is present in ACL Group
can you tell where i am wrong ?
The -eq test compares numeric values. Since $LSEARCH has non-digit characters, it's causing your error.
Switch to [ "$LSEARCH" = " " ] or better yet [ -n "$LSEARCH" ] to test if $LSEARCH has nonzero length,
Handy reference for ksh conditional expressions: http://www.well.ox.ac.uk/~johnb/comp/unix/man_ksh.html#condexp
if [ "$LSEARCH" -eq " " ]; then
In Korn shell, the -eq operator is for numeric values, not strings. For strings, you should use =, as per the following transcript:
pax$ [ "pax diablo" -eq "pax diablo" ] && echo equal
ksh: [: pax diablo: arithmetic syntax error
pax$ [ "pax diablo" = "pax diablo" ] && echo equal
equal
pax$ [ 42 -eq 42 ] && echo equal
equal
As an aside, you may want to look into using the return value from ldapsearch rather than trying to parse the output (which is more likely to change). One way to achieve this is to do the search, ignoring output, and check the return code:
ldapsearch whatever >/dev/null 2>&1
rc=$? ; [ $rc -ne 0 ] && echo ldapsearch failed with error $rc
This is the script I came up with
#!/bin/bash
expression=$1
field=$2
if [ -z "$expression" ]; then
echo "expression is missing"
exit 1
fi
if [ -f /home/miked/table ]; then
if [ -f table ] && [ grep "$expression table" ]; then
grep "$expression" table | cut -d: -f${2:-3} | clipit
else
echo "no match found"
fi
else
echo "there is no table file"
fi
As a matter of fact I know how to fix it, but I don't know why
it's fixed.
If I remove the space between grep and ", all is working fine, I just can't seem to understand why.
If I do grep something file directly to the command line, it's working good. Why do I to stick the grep to the " in the script?
You do not need to wrap grep call inside square brackets. [ is alias to test command (in general, however most shells replicate that using builtin). You can see syntax of that command using man test. What you want do is to check if $expression table exist in some file, so instead you need to write it as:
#!/bin/bash
expression="$1"
field="$2"
if [ -z "$expression" ]; then
echo "expression is missing"
exit 1
fi
if [ -f /home/miked/table ]; then
if [ -f table ] && grep "$expression table"; then
grep "$expression" table | cut -d: -f${2:-3} | clipit
else
echo "no match found"
fi
else
echo "there is no table file"
fi
However there are more problems with your script.
You print errors to stdout instead of stderr which makes them invisible when piping output of your script to other tools, instead you should use echo "error" >&2, ideally use separate function for that.
You pass only 1 argument to grep and I believe that there should be 2: grep "$expression" table.
Your first grep call will also print to stdout and I believe you want to surpass that, so instead use -q flag.
It is good idea to enable "exit on error" using set -e and "exit on pipe error" using set -o pipefail
You do not use outer file, so you can just remove check for that.
You do not use your $field variable.
Use guard clausules instead of ifs for fatal error checking as it will make easier to refactor your script.
So whole file could be written as:
#!/bin/bash
set -eo pipefail
perror() {
echo "$1" >&2 && exit 1
}
expression=$1
field=${2:-3}
file=${3:table}
[ -z "$expression" ] || perror "expression is missing"
[ -f "$file" ] || perror "there is no '$file' file"
grep "$expression" "$file" | cut -d: -f"${field}" | clipit || perror "no match found"
You don't need [ ] at all here. You're interested in the exit status of grep and it gives you one already; to suppress its output, you can use the -q option:
if [ -f table ] && grep -q "$expression" table; then
The filename should not be within the quotes, either.
If you use [ ] without any test, it defaults to -n: "true if string is not empty". This test expects a single argument, which is why it seems to work for you if you remove the space: it just checks if the string grep$expression table expands to is non-zero, and it always is.
There are quite a few things that could be done to fix this, your main problem is the following line:
if [ -f table ] && [ grep "$expression table" ]; then
You've already tested if "table" exists, so you're doing it again and once it succeeds, the expression [ grep "$expression table" ] is being evaluated, which is broken down to '[' grep 'expression table' ']' which means essentially nothing.
You should instead use $() and evaluate the number of occurrences, or like Benjamin mentions, skip it altogether if that's what you want.
I would suggest something like this
#!/bin/bash
expression=$1
field=$2
table_file=/home/miked/table
if [ -z "$expression" ]; then
echo "expression is missing"
exit 1
fi
if [ -f $table_file ]; then
if [ $(grep -q "$expression $table_file") -gt 0 ]; then
grep "$expression" $table_file | cut -d: -f${2:-3} | clipit
else
echo "no match found"
fi
else
echo "there is no table file"
fi
Notice how we're still using a test, this could be condensed to:
#!/bin/bash
expression=$1
field=$2
table_file=/home/miked/table
if [ -z "$expression" ]; then
echo "expression is missing"
exit 1
fi
if [ -f $table_file ]; then
grep -q $expression $table_file && grep "$expression" $table_file | cut -d: -f${2:-3} | clipit || echo "no match found"
else
echo "there is no table file"
fi
A little history behind this - I'm trying to write a nagios plugin to detect if an nfs mount is unmounted and if a mount is stale, which is where I'm running into a problem.
What I'm trying to achieve is detecting if a mount is stale. The problem I'm trying to work around is the fact that a stale nfs handle causes any action on that directory to hang and timeout after 3-4 minutes. By forcing a timeout onto a stat command inside an nfs mounted directory with read, I should be able to work around that problem.
So I picked up this snippet somewhere, which works perfectly when run manually from the cli on an nfs client (where /www/logs/foo is a stale nfs mount)
$ read -t 2 < <(stat -t /www/logs/foo/*); echo $?
1
The problem comes when I try to incorporate this snippet into a script like so (snippet attached, full script attached at the end):
list_of_mounts=$(grep nfs /etc/fstab | grep -v ^# | awk '{print $2'} | xargs)
exitstatus $LINENO
for X in $list_of_mounts; do
AM_I_EXCLUDED=`echo " $* " | grep " $X " -q; echo $?`
if [ "$AM_I_EXCLUDED" -eq "0" ]; then
echo "" >> /dev/null
#check to see if mount is mounted according to /proc/mounts
elif [ ! `grep --quiet "$X " /proc/mounts; echo $?` -eq 0 ]; then
#mount is not mounted at all, add to list to remount
remount_list=`echo $remount_list $X`;
#now make sure its not stale
elif [ ! "`read -t 2 < <(stat -t $X/*) ; echo $?`" -eq "0" ]; then
stalemount_list=`echo $stalemount_list $X`
fi
Gives me this error:
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: syntax error near unexpected token `<'
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: `read -t 2 < <( '
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: syntax error near unexpected token `)'
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: command substitution: line 46: ` ) ; echo $?'
/usr/lib64/nagios/plugins/check_nfs_mounts.sh: line 46: [: stat -t /www/logs/foo/*: integer expression expected
I was able to work around the syntax error by using " read -t 2<<< $(stat -t $X/)" instead of " read -t 2< <(stat -t $X/)", however stat no longer benefits from the timeout on read, which takes me back to the original problem.
While I'm open to new solutions, I'm also curious as to what behavior might be causing this shell vs script difference.
Full nagios check:
#!/bin/bash
usage() {
echo "
Usage:
check_nfs_mounts.sh
It just works.
Optional: include an argument to exclude that mount point
"
}
ok() {
echo "OK - $*"; exit 0
exit
}
warning() {
echo "WARNING - $*"; exit 1
exit
}
critical() {
echo "CRITICAL - $*"; exit 2
exit
}
unknown() {
echo "UNKNOWN - $*"; exit 3
exit
}
exitstatus() {
if [ ! "$?" -eq "0" ] ;
then unknown "Plugin failure - exit code not OK - error line $*"
fi
}
# Get Mounts
list_of_mounts=$(grep nfs /etc/fstab | grep -v ^# | awk '{print $2'} | xargs)
exitstatus $LINENO
for X in $list_of_mounts; do
AM_I_EXCLUDED=`echo " $* " | grep " $X " -q; echo $?`
if [ "$AM_I_EXCLUDED" -eq "0" ]; then
echo "" >> /dev/null
#check to see if mount is mounted according to /proc/mounts
elif [ ! `grep --quiet "$X " /proc/mounts; echo $?` -eq 0 ]; then
#mount is not mounted at all, add to list to remount
remount_list=`echo $remount_list $X`;
#now make sure its not stale
elif [ ! "`read -t 2 <<< $(stat -t $X/*) ; echo $?`" -eq "0" ]; then
stalemount_list=`echo $stalemount_list $X`
fi
done
#Make sure result is a number
if [ -n "$remount_list" ] && [ -n "$stalemount_list" ]; then
critical "Not mounted: $remount_list , Stale mounts: $stalemount_list"
elif [ -n "$remount_list" ] && [ -z "$stalemount_list"]; then
critical "Not mounted: $remount_list"
elif [ -n "$stalemount_list" ] && [ -n "$remount_list" ]; then
critical "Stale mount: $stalemount_list"
elif [ -z "$stalemount_list" ] && [ -z "$remount_list" ]; then
ok "All mounts mounted"
fi
You need to make sure your shebang specifies Bash:
#!/bin/bash
The reason for the error message is that on your system, Bash is symlinked to /bin/sh which is used when there's no shebang or when it's #!/bin/sh.
In this case, Bash is run as if you had started it with bash --posix which disables some non-POSIX features such as process substitution (<()), but confusingly not others such as here strings (<<<).
Change your shebang and you should be OK.
You can save the output of a subshell in this way:
$ read a < <(echo one)
$ echo $a
one
Or in this way (if you just want to process $a and forget it:
$ ( echo one; echo two) | (read a; echo $a)
one
The first variant will work only in bash. Bourne Shell (/bin/sh) does not support this syntax. May be that is the reason why you get the error message. May be you script is interpreted by /bin/sh not by /bin/bash
I have a bash script that has this function in it:
function start_vi()
{
echo "Please enter a file name with complete path to open in vi:"
read input_file
if [ -d "$input_file" ]
then
echo "You entered a directory."
echo "Please try again and enter a readable/writable file."
fi
grep_var="file $input_file | grep -c data"
if [ $? -eq 0 ]
then
vi $input_file
else
echo "File not found or invalid file type. Please try again."
fi
}
For the most part the function works fine. My problem is that the two if statements work fine independently, eg, if I comment out one of them, the test works and it does what I want. But together, as written, for example, when I type in a directory at the prompt, vi opens it as a file, where the test should return an echo saying that it's a directory, as it does when functioning alone.
Any ideas on why this is? I'm still relatively new at bash scripting so it is probably easy for the pros, but I have been banging my head against the wall for a while now.
Thanks in advance.
Add a return statement in the first if/then:
function start_vi()
{
echo "Please enter a file name with complete path to open in vi:"
read input_file
if [ -d "$input_file" ]
then
echo "You entered a directory."
echo "Please try again and enter a readable/writable file."
return
fi
grep_var="file $input_file | grep -c data"
if [ $? -eq 0 ]
then
vi $input_file
else
echo "File not found or invalid file type. Please try again."
fi
}
Otherwise, it will print and then open the file anyway, as your second test should be like this:
file $input_file | grep -c data
if [ $? -eq 0 ]
The $? is the exit code of the last run command. Assigning to a variable (i.e. grep_var="...") sets $? to 0. What you seem to have wanted is the exit code of grep -c data - in that case, use backticks ` instead of quotes " to run commands, like below. Or you can write it like this:
grep_var=`file $input_file | grep -c data`
if [ $grep_var != 0 ]
to compare the string value (i.e. what grep -c data returns - the count of data lines).
Doing some of the above should solve the problem.
You need a loop
function start_vi()
{
echo "Please enter a file name with complete path to open in vi:"
read input_file
while [ -d "$input_file" ]
do
echo "You entered a directory."
echo "Please try again and enter a readable/writable file."
read input_file
done
grep_var="file $input_file | grep -c data"
if [ $? -eq 0 ]
then
vi $input_file
else
echo "File not found or invalid file type. Please try again."
fi
}
All you need is a loop:
....
read input_file
while [ ! -f "$input_file" ]
do
echo "You did not enter a file"
echo "Please try again and enter a readable/writable file."
read input_file
done
grep_var="file $input_file | grep -c data"
if [ $? -eq 0 ]
then
vi $input_file
else
echo "File not found or invalid file type. Please try again."
fi
I think this may be closer to what you want to do..
function start_vi()
{
echo "Please enter a file name with complete path to open in vi:"
read input_file
grep_var=`file $input_file 2>&1 | grep -c data`
while [ $? -ne 0 ]
do
echo "File not found or invalid file type. Please try again."
read input_file
grep_var=`file $input_file 2>&1 | grep -c data`
done
vi $input_file
}