views:

66

answers:

6

I have the following UNIX statement:

#!/bin/bash
$x=$((grep ^'[a-z]' $1 | wc -l))
echo "$x"

However, I'm getting an error message relating to a missing operand whenever I try to run the script. Is there any way to assign a variable a value like so in UNIX?

EDIT:

Well, it became clear to me that grep cannot seem to examine single words, which is what I originally intended to do. Is there a UNIX command that anyone can point me to that deals with searching for a pattern in a word? I'm trying to make some unix code that can tell if a word passed to a script starts with a number. What UNIX call would be the most helpful?

A: 

Instead of wc you can use grep -c.

lhf
+1  A: 

You don't put $ in front of the variable being assigned:

> $x=$(echo -en "1\n2\n3" | grep 3)
bash: =3: command not found
> x=$(echo -en "1\n2\n3" | grep 3)
> echo $x
3

When you write $x before $x exists, it seems to be expanded to nothing; this is why the first line is fully evaluated to just =3.

Mark Rushakoff
Unless you `set -u` which raises an error if you try to use an unset variable.
glenn jackman
A: 

This should work ok

#!/bin/bash
x=$(grep ^'[a-z]' $1 | wc -l)
echo "$x"

Note that $ is not needed when assigning to variables and the extra () is not needed as well.

Coding District
Okay, this seemed to have gotten rid of the original error, but now I get a message that says grep cannot open the argument passed. The argument passed is a word. Is there a alternative to grep that searches through words instead of files?
Waffles
@Waffles: `x=$(echo "$1" | grep '^[a-z]' | wc -l)`
Dennis Williamson
+3  A: 

I believe your remaining problem is that $( ... ) and $(( ... )) are two very different things. The former does command substitution, which is what you want. The latter does arithmetic. Try this:

#! /bin/sh

x=$(grep -c '^[a-z]' "$1")
echo "$x"

I also don't know why you had the caret outside the single quotes; that could cause problems with some shells (where caret either does history expansion or is an alias for |), I think bash is not one of those shells but there's no reason to tempt fate.

Obligatory tangential advice: When expanding shell variables, ALWAYS put them inside double quotes, unless you specifically need word splitting to happen. (This is why I changed $1 to "$1". Without that change, your script would break if you gave it a file with a space in its name.)

EDIT: Now you say you want to know if a word passed to a script starts with a number. By "passed to a script" I am going to assume you mean like this:

./script WORD

The easiest way to do that does not involve grep or command substitution at all:

#! /bin/sh

case "$1" in
    [0-9]*)  
        echo "Starts with a number"
    ;;
    *)
        echo "Doesn't start with a number"
    ;;
esac

Yes, the "case" syntax is gratuitously different from everything else in shell; if you want an internally consistent language, Python is waiting for you thataway. The patterns you put before each open paren are globs, not regexes. If you need a regex, you have to do something more complicated, e.g.

#! /bin/sh

if [ $(expr "$1" : '^[0-9]\{3,5\}x') -gt 0 ]; then
    echo "Starts with a three-to-five digit number followed by an x"
fi

Some people say that it is unnecessary to put quotes around the variable expansion that goes between case and in. They are technically correct but should be ignored anyway, because that's one too many warts to remember. Other people say that you can put an open parenthesis before each glob and then your editor will not get grumpy about the mismatched parentheses. I approve of editor non-grumpiness, but I don't know how portable that variation is.

Zack
"but I don't know how portable that variation is" -- is is portable in the sense that it doesn't violate the standard.
Roman Cheplyaka
Which standard? POSIX.1-2001 (*not* -2008) is a decent *guideline* to portable shell nowadays, but there are so many exceptions to be memorized, and this might be one of 'em. (And of course it matters which shambling revenant proprietary Unix variants you do, or do not, care about.)
Zack
A: 
x=$(grep '^[a-z]' "$1" | wc -l)

or just one awk command

x=$(awk '/^[a-z]/{c++}END{print c}')
ghostdog74
+1  A: 

Zack has the right approach using case. bash has more pattern matching operators:

shopt -s extglob

var="foobar"
if [[ "$var" == @([a-z]*) ]]; then
    echo "var starts with a letter"
fi

In the bash man page, read about [[ under "Compound Commands", and the "Pattern Matching" section.

glenn jackman