I'm having trouble getting the following behaviour:

input='somestring... --key1 val1 --key2 val2 --key3 val3 somestring...'

I tried using:

echo $input | sed -nE 's/.*--key1 (.*) (--.*)$/\1/p' 
#but this gives 'val1 --key2 val2' instead of 'val1' 

Basically, my issue is that I do not know how to tell sed to match the pattern in reverse order (so that --key2 is not captured in \1). I believe there is a simple way here but I cannot find it?

edit: a fix could be to use:

echo $input | sed -nE 's/.*--key1 ([^(--)]*) (--.*)$/\1/p' 

But I would like to know if a "better" solution exists for this use case?

1 Answer 1


That's because (.*) is too greedy.

perl has non-greedy matching:

perl -nE 'if (/--key1 (.*?) --/) {say $1}' <<< "$input"

If you actually want to use proper option parsing:

eval set -- "$(getopt --long key1:,key2:,key3: -- $input)"   # $input is unquoted!

while :; do
    case "$1" in
        --key1) key1=$2; shift 2;;
        --key2) key2=$2; shift 2;;
        --key3) key3=$2; shift 2;;
        --) shift; break;;

[[ "$output" = "$key1" ]] && echo OK
  • So sed has no equivalent to (.*?) non-greedy matching... ahh, too bad! I could use getopt version but I tend to think it too much verbose, so I'll keep my sed ugly solution for yet!
    – bagage
    Commented Jul 7, 2015 at 12:19
  • If you know the argument has no whitespace, you could write: sed 's/.*--key1 ([^[:blank:]]\+).*/\1/' but that's a big assumption IMO. Commented Jul 7, 2015 at 12:35
  • Indeed, argument can contain actually everything (even --) :/...
    – bagage
    Commented Jul 7, 2015 at 13:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .