user2534033
user2534033

Reputation: 69

Special meaning of "$a$" in grep command

I am trying to learn shell scripting, and I am trying different things. While I was practicing I came across grep "$a$" file1 and couldn't understand the output.

I know the difference that single quotes takes the literal meaning and the double quoting tries to find the special meaning like the $a is supposed to be variable $.

I have file1 with content

#!/bin/sh
a=1
b=1
echo $a
echo $b
for I in 1 2 3 4 5 6 7 8
do
    c=a
    b=$a
    b=$(($a+$c))
    echo $b
done

grep "$a$" file1 gives me the whole file1 as output where grep '$a$' file1 gives me output as it is supposed to give, like the line which ends in $a.

Please explain why it gives the whole file content as output when grep "$a$" is used.

Upvotes: 1

Views: 792

Answers (3)

Ruslan Osmanov
Ruslan Osmanov

Reputation: 21492

"$a$"

The string "$a$" is expanded to the value of $a shell variable followed by literal $ (why? see below). If the $a variable is unset, its value is interpreted as an empty string, and the result of expansion is "$" (only the dollar sign).

Why the last dollar sign is not expanded?**

The basic form of parameter expansion is "${PARAMETER}". The braces are not required, if PARAMETER is not a positional parameter with more than one digit (10, 11, etc.), and if PARAMETER is not followed by a character that is not to be interpreted as part of its name. Since $ begins next parameter expansion, command substitution, or arithmetic expansion, it is not interpreted as a part of the a parameter name in our case.

Since nothing is followed by the last $, it doesn't conform to any kind of expansion, and left intact.

How Grep interprets the pattern

Since the match-end-of-line operator ($) matches the empty string either at the end of the string or before a newline character in the string, the pattern $ matches any line.

Therefore, grep "$a$" file will match and print all lines in file, if $a is empty.

'$a$'

Single quotes protect the string from any expansion (interpretation of special characters). That is, the string is passed to the command as is.

In the case of grep '$a$' file invocation, the pattern matches "$a" string at the end of the line. The following describes why.

The last $ means the end of the line, as we know. The rest of dollar signs are interpreted depending on the pattern. It can be interpreted either as literal dollar sign, or the end of the line.

In the following cases, $ represents the end-of-line operator. Otherwise, $ is ordinary.

  • If the $ is last in the pattern, as in foo$.
  • The syntax bit RE_CONTEXT_INDEP_ANCHORS is set, and is outside a bracket expression.
  • It ends an open-group or alternation operator expression, e.g. '\(b$\)', or '\(b$\)\|a'.

Since

  • the first $ in our pattern is obviously not last,
  • you are using the basic RE_SYNTAX_GREP syntax, i.e. BRE,
  • and the open-group/alternation is not used,

the first dollar sign is ordinary in $a$.

When the first $ is not ordinary?

It is not ordinary, for example, in the case of extended regular expressions. That is, with the -E or -P options, or with egrep or pgrep commands, for instance (where the RE_CONTEXT_INDEP_ANCHORS syntax bit is set).

Escaping the dollar sign

Consider escaping the dollar sign even though it is interpreted as literal $ in the basic regular expressions. It will develop a good habit.

To escape the dollar sign within single quotes use a backslash: '\$a$'.

Within double quotes the backslash has special meaning, so you need to escape the backslash itself: "\\\$a\$".

Upvotes: 0

xhienne
xhienne

Reputation: 6144

There is no special meaning.

I don't see any grep in your code. I assume your are grepping from the command line and that the variable a is undefined. Consequently, grep "$a$" expands to grep "$" ($a expands to nothing) and grep $ matches every line since $ matches the end of line.

[update] By 'expand', I mean shell variable expansion. Because $a is between double quotes, the shell is replacing $a with the value of variable a (which is undefined). Your grep '$a$' yields the expected result because any string between single quotes is always left untouched by the shell. Try echo "$a$" vs echo '$a$'.

Upvotes: 1

sjsam
sjsam

Reputation: 21965

To begin with ^ and $ in a grep pattern symbolizes beginning and end respectively.

In

grep "$a$" file1

$a undergoes expansion because it is inside double quotes. In your case $a should be undefined. So the The result of "$a$" is $ which matches the end of every line, so you will get the entire file as output. To verify this assign some test value to a and then run grep.

a="TestValue" # Before running grep
grep "$a$" file1

I bet you'll get nothing as output.

Now if you want to have a literal '$' inside the double quote. then you need to do

grep "\$a$" file1 # See the first $ is escaped

Above command will give you all the lines that end with $a

echo $a
b=$a

Now if you're tired of escaping you may very well use single quotes as below

grep '$a$' file1
# The first $ is literal $, and the last one symbolizes end of file.
# More over variable-expansion doesn't take place inside single quotes.

Note : The a=1 inside the file1 has no influence on the grep result as the file1 is just an input to grep.

Upvotes: 1

Related Questions