Error using [[ ]] and -eq - bash

Shell script snippet:
tagSearch= $(grep '^\#ctags$' ./"$1" | wc -l)
if [[ $tagSearch -ne "0" ]]
then
...
fi
Results in:
line 2: /bb/bin/1: Permission denied
Context:
I'm trying to detect whether a particular pattern exists in a file so I can take a particular action.
I understand the error I'm getting, the detection is working but the script is trying to evaluate the result '1' and run the program '1' in my path. This isn't what I want. How do I get the behavior I'm looking for?

The problem is
tagSearch= $(grep '^\#ctags$' ./"$1" | wc -l)
----------^
You can't use spaces around the equal sign; what you're actually doing here is to temporarily set tagSearch to the empty string in the environment, then invoking grep '^\#ctags$' ./"$1" | wc -l, then trying to run that as a command since the $() will have inserted the result into the command line.
tagSearch=$(grep '^\#ctags$' ./"$1" | wc -l)

Variable assignments in the bash shell should not have a space after the equals. Actually it should never have whitespace in it at all. See below.
tagSearch=$(grep '^\#ctags$' "./$1" | wc -l)
if [[ $tagSearch -ne 0 ]]
then
...
fi
Not important to your error but also of note, when using the double bracket syntax, you don't need to quote that zero any more than the variable you are comparing it with.
Actually your whole code could be re-factored using grep's quite mode and evaluating the return code to see if you got any matches:
if grep '^\#ctags$' "./$1"
then
...
fi

Actually you can have that simpler, because the return code of grep will be 0 if something is found (1 otherwise), so you don't need wc -l. And you can just write:
if `grep -q pattern file`; then echo "yes"; else echo "no"; fi;

Related

linux compare two variable

The script found error but it always goes to Else condition "No Found Error". Am I missing how to compare two variables?
ERROR="Error:"
for i in `find /logs -mtime -1`
do
CHECK=`cat $i |grep -i "Error"|cut -f 1 -d " "`
if [ "$CHECK" == $ERROR ]
then
echo "Found Error"
else
echo "Not Found Error"
fi
done
Did you tried something like if [[ "$CHECK" == $ERROR ]] ?
To simply detect error without printing the error message, you can use
CHECK=$(cat $i | grep "Error" | wc -l)
if [[ $CHECK -ne 0 ]]
then
echo "Found error"
else
echo "Not found error"
fi
You are using grep -i for case-insensitive matching, but then testing the result for exact equality with the string Error:. If the case-insensitive matching is important then the exact equality test is not an appropriate complement.
You are also capturing a potentially multi-line output and comparing it to a string that can be the result only of a single-line output.
And you are matching "Error:" anywhere on the line, but assuming that it will appear at the beginning.
Overall, you are going about this a very convoluted way, as grep tells you via its exit status whether it found any matches. For example:
#!/bin/bash
for log in `find /logs -mtime -1`; do
if grep -i -q '^Error:' "$log"; then
echo "Found Error"
else
echo "Not Found Error"
fi
done
There is two things that I would advise and may fix your issue:
Add #!/bin/bash on the first line, to make sure it is interpreted as bash and not sh. Many time I had trouble with comparison because of this
When comparing two variables, uses double brackets ([[ and ]]) Also, if it strings, always put quotes "$ERROR" around it. It's missing for the $ERROR variable.
Look at the other answers also, there are many ways to do the same thing in a much simpler way.
Note: When comparing numbers you should use -eq

Escaping Shebang in grep

in a shell script, i'm trying to find out if another file is a shell script. i'm doing that by grepping the shebang line. but my grep statement doesn't work:
if [[ $($(cat $file) | grep '^#! /bin' | wc -l) -gt 0 ]]
then
echo 'file is a script!'
else
echo "no script"
fi
i always get the error "bash: #!: command not found". i tried several things to escape the shebang but that didn't work.
maybe you can help me with that? :)
cheers,
narf
I would suggest that you change your condition to this:
if grep -q '^#! */bin' "$file"
The -q option to grep is useful in this case as it tells grep to produce no output, exiting successfully if the pattern is matched. This can be used with if directly; there's no need to wrap everything in a test [[ (and especially no need for a useless use of cat).
I also modified your pattern slightly so that the space between #! and /bin is optional.
It's worth noting that this will produce false positives in cases where the match is on a different line of the file, or when another shebang is used. You could work around the first issue by piping head -n 1 to grep, so that only the first line would be checked:
if head -n 1 "$file" | grep -q '^#! */bin'
If you are searching for a known list of shebangs, e.g. /bin/sh and /bin/bash, you could change the pattern to something like ^#! */bin/\(sh\|bash\).

Read file for value, loop until value = $foo?

I'm writing a shell script that greps for $foo then counts the number of occurrences then runs a command. Each time that command is run, there is one less instance of $foo in that file. Uncertain on how to continuously read that file and reduce the value in the variable I set.
$count= `grep -o $foo /some/file |wc -w`
until [ $count -eq 0 ]
do
some_command_that_deletes_foo_in_file
done
However I realize that $count is set once at runtime and is not updated. What I want is $count to be updated to the current count in /some/file while the script is looping through /some/file until there is 0 instances of the phrase I'm grepping for. Uncertain to what the best approach is.
Unless you have additional code that you haven't showed us that depends on $count, you don't actually need to count occurrences; you just need to know whether the string appears in the file. For that, you can write:
while grep -q -- "$foo" /some/file ; do
some_command_that_deletes_foo_in_file
done
(using the fact that grep returns success when it finds the value, and failure when it does not, and using the -q flag to suppress its console output).
You could add the grep command inside the loop:
count=$(grep -o "$foo" /some/file |wc -w)
until (( count == 0 ))
do
some_command_that_deletes_foo_in_file
count=$(grep -o "$foo" /some/file |wc -w)
done
You simply want to delete the string "$foo"? Use sed:
sed "/$foo/d" /some/file > /some/other/file
The sed command is an editor. The /$foo/ is taking a regular expression (whatever the value of $foo), finding it in the file. The d tells it to delete the line.
sed doesn't usually do an in place edit. You usually have to write to another file and then to a move. However, some sed commands may have such a parameter. You can check your manage.
Second Try
I think it must take some action or perform some processing or something, and one of its effects is that one of the $foos is gone. (But I could be wrong.) – ruakh yesterday
This is what I get answering these questions at night.
You can take advantage of the fact that grep returns true (i.e. exit value = 0) if it can find your regular expression and false (i.e. exit value not equal to 0) otherwise:
while grep -o -q "$foo" "/some/file"
do
some_command_that_deletes_foo_in_file
done
The -q tells grep not to output anything. Instead, the exit value of grep will be true if the string is found and false if it isn't.
Read your manpage on grep. Some grep commands don't have the -q parameter. In that case, you'l need to redirect both STDOUT and STDERR to /dev/null.
Another tact may be to do your count of the number of lines, and then use that as a counter:
count=$(grep -o "$foo" "/some/file" | wc -w) # $(...) is the preferred syntax over `...`
for loop in {1..$count}
do
some_command_that_deletes_foo_in_file
done
The advantage is that you only grep through the file once (which maybe a long operation). However, your count maybe incorrect if $foo is on more than one line.
A few notes:
The $(...) is preferred over backticks
When you set a variable, you don't use the dollar sign in front of that variable. Note I have count= and not $count=.
Watch spaces. count= $(...) is wrong because there's a space after the equals sign.

shell script grep to grep a string

The output is blank fr the below script. What is it missing? I am trying to grep a string
#!/bin/ksh
file=$abc_def_APP_13.4.5.2
if grep -q abc_def_APP $file; then
echo "File Found"
else
echo "File not Found"
fi
In bash, use the <<< redirection from a string (a 'Here string'):
if grep -q abc_def_APP <<< $file
In other shells, you may need to use:
if echo $file | grep -q abc_def_APP
I put my then on the next line; if you want your then on the same line, then add ; then after what I wrote.
Note that this assignment:
file=$abc_def_APP_13.4.5.2
is pretty odd; it takes the value of an environment variable ${abc_def_APP_13} and adds .4.5.2 to the end (it must be an env var since we can see the start of the script). You probably intended to write:
file=abc_def_APP_13.4.5.2
In general, you should enclose references to variables holding file names in double quotes to avoid problems with spaces etc in the file names. It is not critical here, but good practices are good practices:
if grep -q abc_def_APP <<< "$file"
if echo "$file" | grep -q abc_def_APP
Yuck! Use the shell's string matching
if [[ "$file" == *abc_def_APP* ]]; then ...

Echo: Argument List too long

I'm running into an issue where my argument list for echo is too long and would like some ideas on how to get around this issue, or at least test for the condition so I can properly handle it, and it won't kill my script
for file in `cat filelist`; do
PROTOCOLS1=`egrep -i 'rsh|rsync|remsh' "$file" | egrep -v '^[ | ]*#'`
FIELDS=`echo $PROTOCOLS1|wc -l`
if [[ $FIELDS -gt 1024 ]]; then
echo $file >> $debuglog
else
set -A myarray $PROTOCOLS1
do stuff.....
fi
done
So the problem is that when my arg list for echo is too long, $FIELDS is set to null, and thus my test for $FIELDS -gt 1024 always is true and does not get caught.
Problem is when it goes to the array it's obviously too big and I get a subscript out of range error and my script exits.
Any ideas are greatly appreciated.
Edit 9/18
OK so the problem is a little more basic.
myserver-v1> echo $variable
myserver-v1> /usr/bin/echo: too many args
I want to test for this in my script
I tried the following, which works, but I get all this crap to stdout, which fills up my debug log and is annoying
echo $variable
if [[ $? -ne 0 ]]; then
write to error log
fi
Is there a way to test echo $variable....without sending it to stdout?
I tried the following, but neither seemed to work, so I am kind of at a loss here.
[[ ! `echo $variable ]]
[[ `echo $variable ]]
If you keep the unquoted variable $PROTOCOLS1 in the echo, you could simplify life by replacing:
FIELDS=`echo $PROTOCOLS1|wc -l`
with
FIELDS=1
This is because when you echo $PROTOCOLS1 without any quotes around it, you will only have one (possibly very long) line of output. Alternatively, you can use:
FIELDS=$(echo "$PROTOCOLS1" | wc -l)
where the double quotes will preserve the newlines in the value of PROTOCOLS1 (but it gets you back to the 'argument list too long' problem).
So, you need to think about using:
FIELDS=$(egrep -i 'rsh|rsync|remsh' "$file" | egrep -c -v '^[ | ]*#')
which gets the second egrep to do the line counting for you. Obviously, since the later portion of the script uses $PROTOCOLS1, you will need to re-evaluate the egreps to get the data, but you should think about whether your processing scheme is appropriate. If you are running into a string value that is too long, you are probably not doing the processing in the best way. What the alternatives are depends on what you are trying to do, and the question does not reveal that. It might be appropriate to do the extra processing with a scripting language such as Perl, Python or Ruby.

Resources