4

I need a way (the most portable) in bash, to perform a search of the ~/.netrc file, for a particular machine api.mydomain.com and then on the next line, pull the username value.

The format is:

machine a.mydomain.com
  username foo
  passsword bar
machine api.mydomain.com
  username boo
  password far
machine b.mydomain.com
  username doo
  password car

So, it should matchin api.mydomain.com and return exactly boo from this example.

awk '/api.mydomain.com/{getline; print}' ~/.netrc

Get's me the line I want, but how do I find the username value?

Justin
  • 42,716
  • 77
  • 201
  • 296

7 Answers7

5
$ awk '/api.mydomain.com/{getline; print $2}' ~/.netrc
boo

To capture it in a variable:

$ name=$(awk '/api.mydomain.com/{getline; print $2}' ~/.netrc)
$ echo "$name"
boo

By default, awk splits records (lines) into fields based on whitespace. Thus, on the line username boo, username is assigned to field 1, denoted $1, and boo is assigned to field 2, denoted $2.

John1024
  • 109,961
  • 14
  • 137
  • 171
2

If you like to avoid using the getline fuction use this:

awk '/api.mydomain.com/ {f=NR} f&&f+1==NR {print $2}' ~/.netrc
boo

As Ed write here: avoid using it.
http://awk.info/?tip/getline

This will find the line number of the pattern, and then
when line number is one more, print field #2

Can be shorten some to:

awk '/api.mydomain.com/ {f=NR} f&&f+1==NR&&$0=$2' ~/.netrc

or

awk 'f&&!--f&&$0=$2; /api.mydomain.com/ {f=1}' ~/.netrc

This may be the most robust way to do it.
If there are comments line or blank line after domain, other solution fails.

awk '/api.mydomain.com/ {f=1} f && /username/ {print $2;f=0}' ~/.netrc
boo

If domain is found, set flag f. If flag f is true and next line has username print field #2

Jotne
  • 40,548
  • 12
  • 51
  • 55
1

This sed is as portable as I can make it:

sed -n '
    /machine[   ]\{1,\}api.mydomain.com/ {      
        # we have matched the machine
        :a
        # next line
        n
        # print username, if matched
        s/^[    ]\{1,\}username[        ]\{1,\}//p
        # goto b if matched
        tb
        # else goto a
        ba
        :b
        q
    }
' ~/.netrc

The whitespace in the brackets is a space and a tab character.


Looking at this with fresh eyes, this is what I would write now:

awk -v machine=api.mydomain.com '
    $1 == "machine" {
        if (m)
            # we have already seen the requested domain but did not find a username
            exit 1
        if ($2 == machine) m=1
    }
    m && $1 == "username" {print $2; exit}
' ~/.netrc

or if you like unreadable oneliners

awk '$1=="machine"{if(m)exit 1;if($2==M)m=1} m&&$1=="username"{print $2;exit}' M=api.mydomain.com ~/.netrc
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 1
    This is the only answer so far that will handle a file where the username line comes after the password line. But it won't gracefully handle a machine match with no username and password: it will fall through to the next username line. – Neale Pickett Apr 17 '18 at 14:16
  • Good point. Before looking for "username" I should be looking for "machine" and if found then quit. – glenn jackman Apr 17 '18 at 14:35
1

This vanilla Bourne shell (which includes Bash, KSH, and more) function should parse any valid .netrc file: it handled everything I could think of. Invoke with netrc-fetch MACHINE [FIELD]. For your question, it would be netrc-fetch api.mydomain.com user.

netrc-fetch () {
  cat $HOME/.netrc | awk -v host=$1 -v field=${2:-password} '
    {
      for (i=1; i <= NF; i += 2) {
        j=i+1;
        if ($i == "machine") {
          found = ($j == host);
        } else if (found && ($i == field)) {
          print $j;
          exit;
        }
      }
    }
  '
}
Neale Pickett
  • 196
  • 1
  • 4
0

Here's what you could do:

 awk '/api.mydomain.com/{getline;print $2}' ~/.netrc ##$2 will print username
SMA
  • 36,381
  • 8
  • 49
  • 73
0

I recently stumbled upon this issue and couldn't find an answer which covers the one-line format (i.e. machine x login y password z), and inline or intra-line comments.

What I ended up with is making the file into one line with xargs (so only single spaces remain), then using grep with lookbehind (for the keyword) and lookahead (for the next whitespace or end of line)

xargs < ~/.netrc | grep -oP '(?<=machine api\.domain\.com ).*?(?=( machine)|$)' | grep -oP '(?<=login ).*?(?=\s|$)'

This could be of course developed into a function of sorts with extending the dots in the remote host variable with backslashes, printing the password as well, etc.

get_netrc_entry() {
    machine_entry=$(xargs < ~/.netrc | grep -oP "(?<=machine ${1//./\\.} ).*?(?=( machine)|$)")
    grep -oP '(?<=login ).*?(?=\s|$)' <<<"${machine_entry}"
    grep -oP '(?<=password ).*?(?=\s|$)' <<<"${machine_entry}"
}
engedics
  • 53
  • 1
  • 4
0

Another solution to get user of password:

grep -A2 armdocker.rnd.ericsson.se ~/.config/artifactory.netrc | awk '/login/ {print $2}'

grep -A2 gives back the following 2 lines of a requested machine

laplasz
  • 3,359
  • 1
  • 29
  • 40