How can I substitute everything in a string with sed except % and any number directly succeding it? Meaning, everything except strings such as:

%1 %1000 %55 etc.

Given strings of this form:

    1: [18x14] [history 1/2000, 268 bytes] %3
    2: [18x14] [history 1/2000, 268 bytes] %4 (active)

I only want to get the %3 and %4 parts. The numbers can go up to 999.

  • 1
    Some sample input and expected output might help. As of now, the question isn't very clear. – devnull May 5 '14 at 19:37
  • Sorry for that. Given strings of this form: 1: [18x14] [history 1/2000, 268 bytes] %3 and 2: [18x14] [history 1/2000, 268 bytes] %4 (active) I only want to get the %3 and %4 parts. The numbers can be up to 999. – lord.garbage May 5 '14 at 19:39
  • does each line contain only one % sign? – miracle173 May 6 '14 at 7:03
  • Yes @miracle173, each line does only contain one % sign. – lord.garbage May 6 '14 at 10:22
$ sed 's/^.*\(%[0-9]\+\).*$/\1/' input

Assuming that a line contains at most one of those %123 tokens and that every line contains such a token.

The \( \) meta character mark a match-group - which is then referenced in the substitution via the \1 back-reference. ^/$ match the beginning/end of a line.

Otherwise you can pre-filter the input, e.g.:

$ grep '%[0-9]\+' input | sed 's/^.*\(%[0-9]\+\).*$/\1/'

(when not all lines contain such a token)

Another variant:

$ sed 's/\(%[0-9]\+\)/\n\1\n/g' | grep '%[0-9]'

(when a line may contain multiple of those tokens)

Here are line breaks inserted directly before and after each token - in the first part of the pipe. Then the grep part removes all non %123 token lines.

| improve this answer | |
  • + is a gnu extension... without that (on bsd / os x) it would be sed 's/^.*\(%[0-9]\{1,\}\).*$/\1/' input – masukomi Jun 9 '16 at 13:52

You might be better off using grep -o in this case:

grep -oP '\B%[0-9]{1,3}\b' inputfile

Assuming that your version of grep supports Perl compatible regular expressions (-P). Otherwise:

grep -o '\B%[0-9]\{1,3\}\b' inputfile

Using GNU sed, one could transliterate spaces to newlines and get the desired lines:

sed 'y/ /\n/' inputfile | sed '/^%[0-9]\{1,\}/!d'
| improve this answer | |

When working with sed it's almost always advisable to:

/address then/s/earch/replace/

There are two reasons for this. The first is that with multiple lines /addressing/ is faster - it's optimized only to find a match and doesn't bother selecting only portions of a line for editing and so it can narrow the results sooner.

The second reason is that you can play multiple edit operations off of the same address - it makes things much easier.

Of course, in this case, given only the data you show, it makes no practical difference. Still, this is how I would do the thing you ask about:

sed '/^[^%]*\|[^0-9]*$/s///g' <<\DATA
    1: [18x14] [history 1/2000, 268 bytes] %3
    2: [18x14] [history 1/2000, 268 bytes] %4 (active)


It just selects all characters that are non-% characters from the beginning of the line and all non-numeric characters from the end of the line in the address and then removes them with s/// - and that that's that.

In it's current form it might mangle data in unexpected ways if you feed it lines not containing a %digit combo - and that's why addressing is important. If we alter it a little:


It gets safer and faster.

| improve this answer | |

My solution doesn't use sed but grep with extended-regex and only-matching options.

$ cat file
1: [18x14] [history 1/2000, 268 bytes] %3
2: [18x14] [history 1/2000, 268 bytes] %4 (active)
$ cat file | grep -Eo '%[0-9]+'

Using grep in this case is simpler than using sed.

| improve this answer | |
  • 1
    I would tend to agree about the simpler part - and that it is also faster, but... why do you need E? Or cat? If you want to ensure at least one numeric match the + is not necessary: grep -o '%[0-9][0-9]*'- and of course grep's primary purpose is to read files - that's why it works on |pipe files - so grep -${opts} ${files} is all you need, I think. – mikeserv May 7 '14 at 11:36
  • 1
    I reuse cat from the previous command line. I use E option and + for regexp. %x can go up to %999 in the question. grep -o '%[0-9]\+' file and grep -oE '%[0-9]+' file are equivalent for numbers over 100. – kitekat75 May 9 '14 at 6:59

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.