how to compare umask - linux

I am trying to compare the umask of an user. I am getting an error while doing the comparison. The code I am using is
val=`su - user -c "umask" | tail -2 | sed -n "/[0-9]/p"`
if [ $val -eq 744 ]
then
echo "477 found."
fi
When I execute this I am geting an error like:
sh: ^[H: A test command parameter is not valid.
I've tried with = in the compare command, but it is still not working.
Please give any suggestions.
Regards.
val has been initialised as 0.
I am running this as root, so no login is there.
I've also tried giving quotes.

You should quote the variable name in your test expression:
if [ "$val" -eq 744 ]
See here for why.

Your code executes well on my machine, the only solution I can suggest is to use a slightly different syntax, sometimes different bash version complain about one syntax and accept another one:
val=`su - user -c "umask" | tail -2 | sed -n "/[0-9]/p"`
if [[ $val -eq 477 ]] ; then
echo "477 found."
fi
Have a look here for the difference between [ cond ] and [[ cond ]].

Related

Beginner's Bash Scripting: "Unary Operator Expected" error && how to use grep to search for a variable from the output of a command?

I'm attempting to write a bash script that is executed through "./filename username" and checks whether or not that user is logged in, printing the result. I'm still new to scripting and am having trouble understanding how to make this work.
I'm currently getting the error "line 7: [: ambonill: unary operator expected". What does that mean and how can I go about fixing that error?
Additionally, how would I get grep to work instead of sort | uniq? I'd like to grep for the variable from the output of the command but can't find anything related in the man page.
#! /bin/bash
# This script will take a username as an argument and determine whether they are logged on.
function loggedin {
for u in `who | cut -f1 -d" " | sort | uniq`
do
if [ $u == $1 ]
then
echo "$1 is logged on"
else
echo "$1 is not logged on"
fi
exit 0
done
}
loggedin $u
exit 1
Try to find a simpler solution, like:
#!/bin/bash
echo "$1 is $([ -z "$(w -h $1)" ]&&echo -n not\ )logged on"

How to use grep with single brackets?

I was looking at an answer in another thread about which bracket pair to use with if in a bash script. [[ is less surprising and has more features such as pattern matching (=~) whereas [ and test are built-in and POSIX compliant making them portable.
Recently, I was attempting to test the result of a grep command and it was failing with [: too many arguments. I was using [. But, when I switched to [[ it worked. How would I do such a test with [ in order to maintain the portability?
This is the test that failed:
#!/bin/bash
cat > slew_pattern << EOF
g -x"$
EOF
if [ $(grep -E -f slew_pattern /etc/sysconfig/ntpd) ]; then
echo "slew mode"
else
echo "not slew mode"
fi
And the test that succeeded:
#!/bin/bash
cat > slew_pattern << EOF
g -x"$
EOF
if [[ $(grep -E -f slew_pattern /etc/sysconfig/ntpd) ]]; then
echo "slew mode"
else
echo "not slew mode"
fi
if [ $(grep -E -f slew_pattern /etc/sysconfig/ntpd) ]; then
This command will certainly fail for multiple matches. It will throw an error as the grep output is being split on line ending.
Multiple matches of grep are separated by new line and the test command becomes like:
[ match1 match2 match3 ... ]
which doesn't make much of a sense. You will get different error messages as the number of matches returned by grep (i.e the number of arguments for test command [).
For example:
2 matches will give you unary operator expected error
3 matches will give you binary operator expected error and
more than 3 matches will give you too many arguments error or such, in Bash.
You need to quote variables inside [ to prevent word splitting.
On the other hand, the Bash specific [[ prevents word splitting by default. Thus the grep output doesn't get split on new line and remains a single string which is a valid argument for the test command.
So the solution is to look only at the exit status of grep:
if grep -E -f slew_pattern /etc/sysconfig/ntpd; then
Or use quote when capturing output:
if [ "$(grep -E -f slew_pattern /etc/sysconfig/ntpd)" ]; then
Note:
You don't really need to capture the output here, simply looking at the exit status will suffice.
Additionally, you can suppress output of grep command to be printed with -q option and errors with -s option.

Too many arguments in for

In both for statements, I am getting the following error:
./count_files.sh: line 21: [: too many arguments
./count_files.sh: line 16: [: too many arguments.
Can anyone help me ?
#!/bin/bash
files=($(find /usr/src/linux-headers-3.13.0-34/include/ -type f -name '[aeiou][a-z0-9]*.h'))
count=0
headerfiles=($(find /usr/src/linux-headers-3.13.0-34/include/ -type f -name '[_a-zA-Z0-9]*.h' | grep -v "/linux/"))
for file in "${files[#]}"
do
if ! [ grep -Fxq "linux/err.h" $file ];
then
localcount=0
for header in "${headerfiles[#]}"
do
if [ grep -Fxq $header $file ];
then
localcount=$((localcount+1))
if [ $localcount -eq 3 ];
then
count=$(($count+1))
break
fi
fi
done
localcount=0
fi
done
echo $count
One of the problem lines is:
if ! [ grep -Fxq "linux/err.h" $file ];
The semicolon at the end is unnecessary unless the then is on the same line; it is, however, harmless.
It looks as though you want to execute the grep command and check whether it produces any output. However, you've simply provided the test (aka [) command with four string arguments (plus the closing ] for 5 in total), the second of which is not one of the options recognized by test.
You might have meant to use this:
if ! [ -n "$(grep -Fxq "linux/err.h" "$file")" ]
(unless you meant -z instead of -n; the negations are confusing me). But if you're interested in whether grep found anything, you can simply test the exit status of grep:
if grep -Fxq "linux/err.h" "$file"
Hmmm...the -q is 'quiet' mode; so in fact the string test won't work since grep produces no output. You want the direct test of the exit status, possibly preceded by the ! logical not operator.
You shouldn't use square brackets around the grep.
In shell scripts square brackets are not used for grouping, [ is a command in its own right (an alias for test), and it is the [ command that is complaining that you've given it too many arguments.
Just make the call without brackets
if ! grep ....
Change the fors to whiles with reads:
...
echo "${files}" | while read file ; do
...
echo "${headerfiles}" | while read header ; do
...
done
...
done
...

Arrays in Shell Script, not Bash

I am probably just having a brain fart, but I can not for the life of me figure out how to loop through an array in shell script, not bash. Im sure the answer is on stackoverflow somewhere already, but I can not find a method of doing so without using bash. For my embedded target system bash is not currently an option. Here is an example of what I am attempting to do and the error that is returned.
#!/bin/sh
enable0=1
enable1=1
port=0
while [ ${port} -lt 2 ]; do
if [ ${enable${port}} -eq 1 ]
then
# do some stuff
fi
port=$((port + 1))
done
Whenever I run this script the error "Bad substitution" is returned for line with the if statement. If you guys have any ideas I would greatly appreciate it. Thanks!
a="abc 123 def"
set -- $a
while [ -n "$1" ]; do
echo $1
shift
done
Output via busybox 1.27.2 ash:
abc
123
def
BusyBox provides ash which does not directly provide array support. You could use eval and something like,
#!/bin/busybox sh
enable0=0
enable1=1
for index in 0 1 ; do
eval assign="\$enable$index"
if [ $assign == 1 ]; then
echo "enable$index is enabled"
else
echo "enable$index is disabled"
fi
done
One could use positional parameters for that...
http://pubs.opengroup.org/onlinepubs/009696799/utilities/set.html
#!/bin/sh
enable0=0
enable1=1
set -- $enable0 $enable1
for index in 0 1; do
[ "$1" -eq 1 ] && echo "$1 is enabled." || echo "$1 is disabled."
shift
done
Running on busybox:
~ $ ./test.sh
0 is disabled.
1 is enabled.
It's best not to use eval unless there is no other alternative. (The recent spate of bash exploits is due to the shell internally evaling the contents of environment variables without verifying their contents first). In this case, you seem to be in complete control for the variables involved, but you can iterate over the variable values without using eval.
#!/bin/sh
enable0=1
enable1=1
for port_enabled in "$enable0" "$enable1"; do
if [ "$port_enabled" -eq 1 ]; then
# do some stuff
fi
done

Bash string comparison syntax

I was looking through the /etc/bash_completion script found in some Debian packages. I was interested in using the code that looks through a specific directory (/etc/bash_completion.d/ by default) and sources every file in that directory.
Unfortunately, trying to run the script causes errors under the Mac OS X version of bash. The lines in question are:
for i in $BASH_COMPLETION_DIR/*; do
[[ ${i##*/} != #(*~|*.bak|*.swp|\#*\#|*.dpkg*|.rpm*) ]] &&
[ \( -f $i -o -h $i \) -a -r $i ] && . $i
done
Specifically, my version of bash (3.2.17) chokes on the #() construction. I get that the point of that first test is to make sure we don't source any editor swap files or backups, etc. I'd like to understand exactly what that #() syntax does, and, if possible how to get something similar (and similarly elegant) running on my ancient copy of bash. Can anyone offer insight?
It's just an extension to the shell comparison which is equivalent to the grep "or" operator (|).
Depending on your bash version, it may not be available or you may have to set extglob with the shopt built-in. See the following session transcript:
pax#daemonspawn> $ bash --version
GNU bash, version 3.2.48(21)-release (i686-pc-cygwin)
Copyright (C) 2007 Free Software Foundation, Inc.
pax#daemonspawn> echo #(*~|*.pl)
bash: syntax error near unexpected token '('
pax#daemonspawn> shopt extglob
extglob off
pax#daemonspawn> shopt -s extglob
pax#daemonspawn> echo #(*~|*.pl)
qq.pl qq.sh~ xx.pl
pax#daemonspawn>
That allows the following to work:
?(pattern-list)
Matches zero or one occurrence of the given patterns
*(pattern-list)
Matches zero or more occurrences of the given patterns
+(pattern-list)
Matches one or more occurrences of the given patterns
#(pattern-list)
Matches one of the given patterns
!(pattern-list)
Matches anything except one of the given patterns
If you can't get it working with shopt, you can generate a similar effect with older methods such as:
#!/bin/bash
for i in $BASH_COMPLETION_DIR/*; do
# Ignore VIM, backup, swp, files with all #'s and install package files.
# I think that's the right meaning for the '\#*\#' string.
# I don't know for sure what it's meant to match otherwise.
echo $i | egrep '~$|\.bak$|\.swp$|^#*#$|\.dpkg|\.rpm' >/dev/null 2>&1
if [[ $? == 0 ]] ; then
. $i
fi
done
Alternatively, if there's multiple complex determinations that will decide whether you want it sourced, you can use a doit variable that's initially set to true, and set it to false if any of those conditions trigger. For example, the following script qq.sh:
#!/bin/bash
for i in * ; do
doit=1
# Ignore VIM backups.
echo $i | egrep '~$' >/dev/null 2>&1
if [[ $? -eq 0 ]] ; then
doit=0
fi
# Ignore Perl files.
echo $i | egrep '\.pl$' >/dev/null 2>&1
if [[ $? -eq 0 ]] ; then
doit=0
fi
if [[ ${doit} -eq 1 ]] ; then
echo Processing $i
else
echo Ignoring $i
fi
done
did this in my home directory:
Processing Makefile
Processing binmath.c
: : : : :
Processing qq.in
Ignoring qq.pl --+
Processing qq.sh |
Ignoring qq.sh~ --+--- see?
Processing qqin |
: : : : : |
Ignoring xx.pl --+

Resources