Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I want to test whether a line, read in from a file, has a specific beginning AND an ending containing a word held in a variable. Here's some code:

The input file is:

line one
#; line two
#; line three blah
line four

A minimal script, which fails, is:

declare ENDOFLINE= "blah"
exec 3< "inputfile"

while read LINE <&3
    if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "${ENDOFLINE%$}") )) ]]; 
    echo score!
    else echo no score

But, if I do:

if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "blah$") )) ]];

it succeeds in identifying the correct line (=> #; line three blah). In other words, I need a compound test condition where the first test is whether the beginning of the line is '#;' and the end of the line is a string contained in the variable $ENDOFLINE .

Thanks for any help.

share|improve this question
I'm not familiar with "MWE script". And Google isn't showing anything that seems likely. Can you explain that? – mattdm Apr 6 '11 at 20:27
That said, there's no reason to make this two regexes, is there? – mattdm Apr 6 '11 at 20:32
@mattdm - MWE = 'minimum working example'; Code simplified from my actual script containing the essence of the problem for faster understanding. Most sites where code is discussed require an MWE example. – bev Apr 6 '11 at 20:36
@bev: Don't use non-standard abbreviations if you want to be understood. You should have said which shell you are using (there are three, not counting minor variants, that your script could be in). “MWE” was where one would have expected something like “bash” or “ksh”, which was confusing. – Gilles Apr 6 '11 at 20:40
@mattdm: You might want two regexps for clarity, or because there's a risk that the prefix and suffix overlap. – Gilles Apr 6 '11 at 20:41
up vote 6 down vote accepted

Looks like a simple mistake: ${ENDOFLINE%$} strips a $ at the end of $ENDOFLINE, but what you want to do is use $ENDOFLINE literally and have a $ after it to indicate the end of the line.

if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "${ENDOFLINE}$") )) ]];

This works in zsh but not in ksh or bash. Bash requires the regexp $ to be unquoted (otherwise, it's interpreted literally), and ksh doesn't like double parentheses inside [[ … ]] (it interprets them as an arithmetic instruction). This simpler line works in all three shells (note that [[…]] is one of the few places where you don't need "…" around variable expansions):

if [[ $LINE =~ ^"#;" && $LINE =~ $ENDOFLINE$ ]];

If $ENDOFLINE never overlaps with #; (e.g. if ENDOFLINE is ;foo, you'll be accepting #;;foo but rejecting #;foo), you can reduce this to a single test:

if [[ $LINE =~ ^"#;".*$ENDOFLINE$ ]];

You can also use a wildcard pattern instead of a regexp here:

if [[ $LINE = "#;"*"$ENDOFLINE" ]];

The [[…]] construct doesn't exist in all shells, it's specific to bash, ksh and zsh. On other shells (Bourne, dash, anything POSIX), you can do wildcard matches with the case construct:

case $LINE in
  "#;"*"$ENDOFLINE") echo score;;
  *) echo no score;;
share|improve this answer
Thanks so much. Your 'simpler' version is what I need. Thanks also for the instruction on the syntax of [[...]] . I was unable to find an answer to my question online, and I had tried many variants of the code I posted here. I'm afraid my understanding of regexp in the various languages I code in is frequently found wanting. – bev Apr 6 '11 at 21:03
I appreciate the additions to your original answer, both the 'one test' expression and the wildcard test work in my script, and the wildcard answers another of my questions. I hadn't realized that a wildcard was possible in that context. The 'case' example seems obvious, but I hadn't thought of it. I have another regexp question, since this was was such a success, which I will post in a minute. – bev Apr 6 '11 at 22:57

You can also do this using grep:

while read line; do
    if grep -Eq "^#;.*$end$" <<< "$line"; then
        echo score!
        echo no score

(That's in bash; the syntax may be slightly different in other shells but the principle is the same.)

share|improve this answer
Good point about grep, but not if you're going to use it in a loop. Replace the whole loop with grep -q "^#;.*$end\$" <&3. – Gilles Apr 6 '11 at 21:58
Thanks for your suggestion. I'm putting your answer into my notes file for future reference. Another solution I hadn't considered... – bev Apr 6 '11 at 23:01

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.