BASH function for printing IP addresses for domains
Tags: bash, dns, linux, quickndirty
Published: 2020-04-15 23:18:19

---
title: BASH function for printing IP addresses for domains
published: true
description: Formerly a quick `for...in` loop, but now a function that takes a filename for input and iterates over each line
tags: bash, dns, linux, quickndirty
---

*Updated to include a function alternative to the original snippet further down.*

___

First of all, *dang, it's been a while...* :sweat_smile:

Anyway, I was trying to find a one-off way to print a list of domains and each domain's main A record's IP address. After a little `dig`ing :wink: this is what I ended up with initially.

## Snippet
```bash
#!/usr/bin/env bash
domains=(example.com example.net example.org)
for domain in "${domains[@]}"; do
	echo "$domain : $(dig +short a $domain | tail -n1)"
done
```

### Sample output

```
example.com: 93.184.216.34
example.net: 93.184.216.34
example.org: 93.184.216.34
```

### Explanation

#### BASH arrays
I'm sorry to say I don't really know much on the subject; the handful of times I've needed them in scripting, I've had to look up syntax for creating them and then accessing. [This article][bash-arrays-opensource-com] seems to shed some light on the subject though, including how to iterate over array *indices* instead of only array *elements*.

#### `dig` command
As someone who comes from primarily being a Windows user (only due to work these days), I kept forgetting about this command. It lets you query any kind of DNS records for a domain.

It's also normally pretty verbose too, but passing it the `+short` option cuts down the output. `a` also tells it to only query A records.

#### `tail` command
I don't use this one often, but I have used `head` quite a bit to pull the first 10+ lines of a file if I was looking for some information stored at the top. This command pulls content from the bottom of a file (or output piped to it in this case).

## Function alternative

After digging a bit more and chatting with some local Linux users, I ended up rewriting the above snippet into a function that did the same thing.

```bash
#!/usr/bin/env bash
function list_domain_ips() {
  while read domain; do
    echo "$domain: $(dig +short a $domain | tail -n1)" 
  done < $1
}
```

### Usage
#### Example 1: passing a filename
```bash
# function call
list_domain_ips example.txt
```

```
# example.txt contents
example.com
example.net
example.org
```

#### Example 2: passing `/dev/stdin`
```bash
list_domain_ips /dev/stdin <<< "example.com
example.net
example.org"
```

### Explanation

#### `while` instead of `for...in` loop
This was simpler because the loop will only run as long as the `read` command keeps supplying more input.

#### Last line of the loop: `done < $1`
This line is what's passing input to the loop. In this case, it's passing the first argument sent to the function, `$1`. If the function had second, third, fourth arguments (ad infinitum), they would be `$2`, `$3`, and `$4`, respectively. The first argument is also a filename containing a multi-line list of domains to check.

#### Using `/dev/stdin` as the argument
This is useful if, even though this function expects a filename, you still want to pass a list directly yourself. This part of the solution I found on a [superuser answer][superuser-com]. Through testing, I found that a blank line will result in undesirable output (just something I wanted to mention for any BASH beginners on here). To prevent this, the opening and closing quotes do need to be in contact with lines that need to be used in the loop.

##### Sample output with blank lines
```
: m.root-servers.net. # opening quote on its own line
example.com: 93.184.216.34
example.net: 93.184.216.34
example.org: 93.184.216.34
: m.root-servers.net. # closing quote on its own line
```

Besides that minor issue, the output is formatted exactly the same way.

:warning: **Important note:** I was told by a local Linux wizard :mage: by the name of Jās Eckard that `/dev/stdin` isn't 100% *portable*.
>Just be aware that using /dev/stdin is not portable across all Un*x.  I just read yesterday that it's on Linux and OSX (and even then, symlinks to different files), but not necessarily other Un*x.

I just wanted to include this note from him for reference, in case this doesn't work for a particular user.

## Summary

So initially I was iterating over an array of domain names, but the better solution ended up being the function since it takes either a filename or a multi-line list sent through `/dev/stdin` (and obviously since it's easier to store and call from CLI).

The old `echo` line using a subshell to run the `dig` command and piping the output to `tail`, and prefixing that output with the domain name itself for reference carried over without needing to be changed.

Thanks for reading, and enjoy! :penguin: :computer:

___

## Useful links
- [For and Read-While Loops in Bash](http://www.compciv.org/topics/bash/loops/)
- [You don't know Bash: An introduction to Bash arrays][bash-arrays-opensource-com]
- [superuser answer: Pass text to program expecting a file][superuser-com]

[bash-arrays-opensource-com]: https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays
[superuser-com]:https://superuser.com/a/939797