I have a series of lines form.

Agenda HR-1 Presented by XYZ
HR-2 Debate-1 - All
HR-3 Debate-2 - All
(Cov-4) Conclusion 

Each line has an ID of the (sed) pattern [A-Za-z]\+-[0-9]\+, i.e. one or more alphabets follows by a dash (-) follows by one or more numbers. They occur anywhere in the line.

I need to extract the IDs. My thought was to stick a .* at the beginning and end and print \1, but I can't get it to work.

This reply says sed replaces the first match only and that is correct:

$ cat /tmp/scratch/x | sed -n   's/\<\([A-Za-z]\+-[0-9]\+\)/ID:\1/p'
Agenda ID:HR-1 Presented by XYZ
ID:HR-2 Debate-1 - All
ID:HR-3 Debate-2 - All
(ID:Cov-4) Conclusion

But of course a .* at start would greedily go to the last match:

$ cat /tmp/scratch/x | sed -n   's/.*\<\([A-Za-z]\+-[0-9]\+\).*/ID:\1/p'

The only way I can think of doing this in sed is by adding markers around the ID in one command and extract using another, as follows.

Is there a better way to do this in sed?

$ cat x | sed -n   's/\<\([A-Za-z]\+-[0-9]\+\)/<id>\1<~id>/;s/.*<id>\(.*\)<~id>.*/\1/;p'
  • You don't need to use cat with sed. sed already sends to stdout. Add the expected output to your questiion. Also, ID doesn't appear anywhere in the first block of your sample text. Mar 11, 2020 at 2:23
  • Is sed your only option here? it would be quite straightforward in perl I think Mar 11, 2020 at 2:30
  • @steeldriver I was able get it to work with perl by copying from an answer here but hesitated because I have never used perl much. Mar 11, 2020 at 3:04
  • 1
    @nasir piping from cat is just a style choice in my example. In actual use the input comes from a different command. Not sure I understand your point about ID, I have used it in replacement text. Perhaps my not using it in the last example is the source of confusion? Mar 11, 2020 at 3:07
  • @MiserableVariable You simply don't need to use it. If it's in the file /tmp/scratch/x, then just use sed with the file as an argument. Your first sample of text at the top of your question doesn't have ID. Mar 11, 2020 at 3:19

3 Answers 3


With GNU awk, try:

gawk -v FPAT='[A-Za-z]+-[0-9]+' '$1{print $1}' FILE


gawk -v FPAT='[A-Za-z]+-[0-9]+' '$0=$1' FILE
  • gawk is usually my tool of choice but haven't used FPAT in the longest time if at all. Thank you for this Mar 11, 2020 at 3:03

With sed, try:

<FILE sed -En '/([[:alpha:]]+-[0-9]+).*/{ s//:\1/; s/.*://p; }'
  • Remove everything after the desired match
  • Now the match is at the end of every line – this can be handled in various ways
    • The above is easily readable – place a char which isn't part of the the match before each match, then remove everything up to the last char with the second s command
    • Another option for the second s command is to remove everything up to a character which is not part of the match:
<FILE sed -En '/([[:alpha:]]+-[0-9]+).*/{ s//\1/; s/.*[^[:alnum:]-]//; p; }'
  • Removing everything after the first match is an interesting approach. My current approach is also to use a delimiter not occurring in the ID, so that is something I can relate to. Thanks Mar 11, 2020 at 2:57

With Perl we can do it as follows:

$ perl -lne 'print /([a-z]+-\d+)/i' file



You must log in to answer this question.

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