1

I have a basic script for finding specific installed packages in Linux. If not found -> print the package.

I used grep -w, but it didn't work properly with the - character.

For example,

dpkg -l | grep -w "rpm"

Will also find rpm-common.

I found a solution for that using regex. The problem is it doesn't work in a Bash script.

Solution:

dpkg -l | grep -E '(^|\s)string($|\s)'

Condition:

if [ `dpkg -l | grep -E '(^|\s)$i($|\s)' | wc -l` = 0 ]; then echo$i; fi

$i refers to an array value, like arr[index]=$i.

What escaping characters do I need to use so the condition will work?

1

There are two problems here: as @AFH pointed out, \s is a Perl shorthand, not part of the standard POSIX syntax. The standard way to represent this is with the character class [[:space:]]. The second problem is that variable references aren't expanded inside single-quotes; you need to use double-quotes (and escape the $ that's part of the regex).

Also, a few of stylistic recommendations: $( ) is generally preferred over backticks for command substitution (it's easier to read, and doesn't have some weird escaping syntax oddities that backticks have). But in this case, you can skip the command expansion; rather than using wc -l and comparing to zero to see if there were any matches, just use grep's exit status as your test (and the -q option to keep it from printing the match). Also, be sure to double-quote variable references (e.g. in the echo command).

Here's my recommended rewrite:

if dpkg -l | grep -Eq "(^|[[:space:]])$i(\$|[[:space:]])"; then echo "$i"; fi

EDIT: as igor pointed out in a comment, this won't work with package names that contain regex metacharacters (like g++-5). It's possible to preprocess the package name to escape the metacharacters, but it's rather messy. But there's a simpler way: if you're using bash (and not some other shell), you can use bash's native regex capability to do the matching directly:

if [[ "$(dpkg -l)" =~ (^|[[:space:]])"$i"(\$|[[:space:]]) ]]; then echo "$i"; fi

The reason this works is that the $i in the middle of the pattern is in double-quotes, which tells bash to match it as a literal string (i.e. ignore any regex metacharacters in it).

  • This solution doesn't work for packages with special characters. For example g++-5 or g++-4.9. Any ideas? – igor Nov 19 '17 at 6:39
  • @igor See edit. – Gordon Davisson Nov 19 '17 at 6:56
1

Extended Regular Expressions do not support \s, but Perl REs do, so try using -P instead of -E:

dpkg -l | grep -P '(^|\s)rpm($|\s)'

See this reference for more information.

The grep manual says that -P is experimental, but it works for me on Ubuntu 16.04.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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