6

I'm trying to use a negative lookahead in perl in command line:

echo 1.41.1 | perl -pe "s/(?![0-9]+\.[0-9]+\.)[0-9]$/2/g"

to get an incremented version that looks like this:

1.41.2

but its just returning me:

![0-9]+\.[0-9]+\.: event not found

i've tried it in regex101 (PCRE) and it works fine, so im not sure why it doesn't work here

2

4 Answers 4

6

In Bash, ! is the "history expansion character", except when escaped with a backslash or single-quotes. (Double-quotes do not disable this; that is, history expansion is supported inside double-quotes. See Difference between single and double quotes in Bash)

So, just change your double-quotes to single-quotes:

echo 1.41.1 | perl -pe 's/(?![0-9]+\.[0-9]+\.)[0-9]$/2/g'

and voilà:

1.41.2
Sign up to request clarification or add additional context in comments.

1 Comment

You can also do set +H to disable ! history expansion in the current shell. And shopt -s histreedit will let you edit a failed history expansion instead of just throwing the typed command away.
4

I'm guessing that this expression also might work:

([0-9.]+)\.([0-9]+)

Test

perl -e'
    my $name = "1.41.1";
    $name =~ s/([0-9.]+)\.([0-9]+)/$1\.2/;
    print "$name\n";
    '

Output

1.41.2

Please see the demo here.

2 Comments

thanks emma! this works but single quotes would be the more direct route.
@Allen Um, you have single quotes, too ... but lookarounds are really not needed here
4

If you want to "increment" a number then you can't hard-code the new value but need to capture what is there and increment that

echo "1.41.1" | perl -pe's/[0-9]+\.[0-9]+\.\K([0-9]+)/$1+1/e'

Here /e modifier makes it so that the replacement side is evaluated as code, and we can +1 the captured number, what is then substituted. The \K drops previous matches so we don't need to put them back; see "Lookaround Assertions" in Extended Patterns in perlre.

The lookarounds are sometimes just the thing you want, but they increase the regex complexity (just by being there), can be tricky to get right, and hurt efficiency. They aren't needed here.

The strange output you get is because the double quotes used around the Perl program "invite" the shell to look at what's inside whereby it interprets the ! as history expansion and runs that, as explained in ruakh's post.

Comments

0

As an alternate to lookahead, we can use capture groups, e.g. the following will capture the version number into 3 capture groups.

(\d+)\.(\d+)\.(\d+)

If you wanted to output the captured version number as is, it would be:

\1.\2.\3

And to just replace the 3rd part with the number "2" would be:

\1.\2.2

To adapt this to the OP's question, it would be:

$ echo 1.14.1 | perl -pe 's/(\d+)\.(\d+)\.(\d+)/\1.\2.2/'
1.14.2
$

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.