hertzsprung
hertzsprung

Reputation: 9893

Remove $PATH entry based on regex

I'm trying to remove an entry in $PATH if that entry contains a given string foo. For example PATH=/home/me/bin:/home/foo/bin:/usr/foo/bin:/bin would become PATH=/home/me/bin:/bin. I currently have

echo $PATH | perl -pe 's/:(.*?)foo(.*?):/:/g'

but this seems to be removing too many entries, even though I'm trying to use non-greedy matching. What am I doing wrong?

Upvotes: 0

Views: 104

Answers (5)

Miller
Miller

Reputation: 35198

It's good of you to use non-greedy matching .*? in your below regex.

echo $PATH | perl -pe 's/:(.*?)foo(.*?):/:/g'

However, there is a flaw in your regex because you did not limit the allowed characters, and therefore it can capture across your colon boundaries.

That's one reason why split is a nice tool. It not only enforces boundaries but also the accepted characters between them.

It's of course possible to do this using a regex though, like you intended:

echo $PATH | perl -pe 's/[^:=]*foo[^:]*:?//g'

The above will leave a trailing colon if it's the last entry.

Upvotes: 0

fedorqui
fedorqui

Reputation: 289725

You can also use awk to loop through the paths, printing just the ones that don't contain foo:

awk -F: '{for (i=1; i<=NF; i++) if ($i !~ /foo/) {printf "%s%s", $i, (i==NF? RS : FS)}}'

Explanation

  • -F: sets : as field separator.
  • for (i=1; i<=NF; i++) loop through the fields.
  • if ($i !~ /foo/) if the path does not contain the string foo, then perform next action:
  • printf "%s%s", $i, (i==NF? RS : FS) print the line, ending with new line or : depending on the position it is. If it is the last field, new line; otherwise, :.

Test

$ cat a
/home/me/bin:/home/foo/bin:/usr/foo/bin:/bin
/home/me/bin:/home/fio/bin:/usr/foo/bin:/bin

$ awk -F: '{for (i=1; i<=NF; i++) if ($i !~ /foo/) {printf "%s%s", $i, (i==NF? RS : FS)}}' a
/home/me/bin:/bin
/home/me/bin:/home/fio/bin:/bin

Upvotes: 1

Biffen
Biffen

Reputation: 6355

I started to construct a regex that would deal with all the combinations of colons at either end, but it got complex fast. But since you're using Perl (good choice BTW) how about something like:

perl -e 'print join(":", grep { $_ !~ m/foo/ } split( ":", '"$PATH"' ) );'

Upvotes: 0

Richard RP
Richard RP

Reputation: 525

I would split the string, select the valid parts and then join the string again:

perl -e 'print join ":", grep !/foo/, split ":", $ENV{PATH}'

Upvotes: 5

anubhava
anubhava

Reputation: 785146

You can use:

perl -pe 's/:(.*?)foo[^:]*(?=:|$)//g' <<< "$PATH"
/home/me/bin:/bin

Upvotes: 1

Related Questions