1

I have following file, it's a config file for ssh.

Host vps2 # Linode
    HostName xxx.xx.xx.xxx
    User foo_user

Host vps3 # Vultr
    HostName xxx.xx.xx.xxx
    User foo_user   

Host vps4
    HostName xxx.xx.xx.xxx
    User foo_user

Host vps5
    HostName xxx.xx.xx.xxx
    User foo_user 

Host vps6
    HostName xxx.xx.xx.xxx
    User foo_user 

Host vps7 # DigitalOcean
    HostName xxx.xx.xx.xxx
    User foo_user

Host vps8 # GCP
    HostName xxx.xx.xx.xxx
    User foo_user   

Host pi
   HostName xxx.xx.xx.xxx
   User pi

# OLD SHALL NOT BE USED

Host vps13
    HostName xxx.xx.xx.xxx
    User foo_user

Host vps14-old
   HostName xxx.xx.xx.xxx
   User foo_user 

Host vps4-old
    HostName xxx.xx.xx.xxx
    User foo_user 

Host vps15-old
   HostName xxx.xx.xx.xxx
   User foo_user 

Host vps11-old
    HostName xxx.xx.xx.xxx
    User foo_user

I need to print alias that start with vps*, below (copied) snippets will exactly do that.

$ awk '{for(i=1;i<=NF;i++){if($i~/^vps/){print $i}}}' $HOME/.ssh/config
vps2
vps3
vps4
vps5
vps6
vps7
vps8
vps3-old
vps4-old
vps5-old
vps11-old

Now I want to print all alias that has no -old suffix, adding | grep -v old works.

$ awk '{for(i=1;i<=NF;i++){if($i~/^vps/){print $i}}}' $HOME/.ssh/config | grep -v "old"
vps2
vps3
vps4
vps5
vps6
vps7
vps8

Is there any cleaner way ? Preferably involving only 1 tools, I tried playing with awk command to no avail.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Liso
  • 188
  • 2
  • 14

5 Answers5

5

You can use sed, which has a grep-like mode if you use -n (no print) that supports a more extended regex than grep. The trick for filtering out lines ending in -old is found here: Sed regex and substring negation:

sed -n "/-old/b; s/^Host\s\+\(vps\S*\)\s*\(#.*\)\?/\1/p" $HOME/.ssh/config

The inverse (including -old) is a bit simpler, since it only requires positive matches:

sed -n "s/^Host\s\+\(vps\S*-old\)\s*\(#.*\)\?/\1/p" $HOME/.ssh/config
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • What if I want to only print alias which has `-old` suffix ? – Liso Jan 10 '22 at 05:06
  • Then you remove the first command `/-old/b;` and add `-old` to the first capture group a the end, just like `vps` is at the beginning. – Mad Physicist Jan 10 '22 at 05:07
  • @Liso. I spelled it out in the update – Mad Physicist Jan 10 '22 at 05:36
  • Actually I figured that out, but seeing I need the result parsed in single line (which I have not specified in my original question), [`awk` is much simpler.](https://stackoverflow.com/a/15580184/12289283) – Liso Jan 10 '22 at 05:41
  • I can't (entirely) disagree with awk being simpler :) – Mad Physicist Jan 10 '22 at 05:41
  • 1
    You should mention that requires GNU sed for `\s` and `\+` (and maybe also `\?`, I'm not sure about that one). Since it requires GNU sed anyway you could make it more concise by adding `-E` to enable EREs and then removing many of the backslashes. Btw a good rule of thumb is to always use single quotes around shell strings, including scripts, and only use double quotes or no quotes when you have a specific NEED for the shell to do some interpretation on the string/script. – Ed Morton Jan 10 '22 at 14:54
  • 1
    @EdMorton. Good call on the quotes. That's what happens when I copy and paste from another post :) I'll look into the rest later and fix it – Mad Physicist Jan 10 '22 at 16:00
2

You could add a $i!~/-old$/ condition to the awk command:

awk '{for(i=1;i<=NF;i++){if($i~/^vps/ && $i!~/-old$/){print $i}}}' ~/.ssh/config

(Note: I prefer ~ over $HOME when it's not in double-quotes, just in case of weird characters in the path.)

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
1

This might work for you (GNU sed):

sed -En '/-old/!s/^Host\s*(vps\S*).*/\1/p' file

Turn off implicit printing and on extended regexps by using the -n and -E options.

If a line does not contain -old, match on a line beginning Host followed by some whitespace, followed by vps, followed by zero or more non-whitespace, followed by anything and replace it by vps followed by any non-whitespace and print the result.

To only show lines with -old in them, use:

sed -En '/-old/s/^Host\s*(vps\S*).*/\1/p' file

N.B. This relies on Host lines not containing comments or more than one host name.

potong
  • 55,640
  • 6
  • 51
  • 83
1

Idk why you're looping when the string you want is always in the 2nd field of a line that starts with Host:

$ awk '/^Host vps/ && !/-old/{ print $2 }' file
vps2
vps3
vps4
vps5
vps6
vps7
vps8
vps13

I see in comments you actually want the output all on 1 line, that'd be:

$ awk '/^Host vps/ && !/-old/{ printf "%s%s", sep, $2; sep=OFS } END{print ""}' file
vps2 vps3 vps4 vps5 vps6 vps7 vps8 vps13
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0
grep -Po '(?<=Host )vps\d+(?!-old)' file | sort -uV
konsolebox
  • 72,135
  • 12
  • 99
  • 105