Scripts > Manual Labor

I needed to change the layout of some text. Repetitive work is something I hate, so I wrote a script to do it for me. Doing it manually would have been faster though.

Scripts > Manual Labor
Photo by Caspar Camille Rubin / Unsplash

Simple Plan

I recently had some issues with my AdGuard Home instance. The DHCP server was giving the wrong DNS server to my devices. So I wanted to migrate to another DHCP server.

The biggest part of this migration would be copying the static leases to the new server. As AdGuard Home does not have an option to export them, I copy-pasted them from the web interface in to a text file. It gave me a list that looks something like this (but a lot longer):

aa:aa:aa:aa:aa:aa
10.0.1.111
devicea
aa:aa:aa:aa:aa:ab
10.0.1.112
deviceb
aa:aa:aa:aa:aa:ac
10.0.1.113
devicec
aa:aa:aa:aa:aa:ad
10.0.1.114
deviced

The tool I'm migrating to uses yaml for its configuration and I needed the data in this format:

hosts:
  - mac: aa:aa:aa:aa:aa:aa
    ip: 10.0.1.111
    name: devicea
  - mac: aa:aa:aa:aa:aa:ab
    ip: 10.0.1.112
    name: deviceb
  - mac: aa:aa:aa:aa:aa:ac
    ip: 10.0.1.113
    name: devicec
  - mac: aa:aa:aa:aa:aa:ad
    ip: 10.0.1.114
    name: deviced

I did not want to do this manually so I wrote a script to do it.

The Script

The script looks like this:

#!/bin/bash

# init variables & constants
INFILE=$(cat "$1")
OUTFILE=$2
looper=0

# clear file and write top key
echo "hosts:" | tee "${OUTFILE}"

# loop lines and write key:values
for LINE in $INFILE; do
  if test $looper -eq 0; then
    echo "  - mac: ${LINE}" | tee -a leases.yml
    looper=1
  elif test $looper -eq 1; then
    echo "    ip: ${LINE}" | tee -a leases.yml
    looper=2
  elif test $looper -eq 2; then
    echo "    name: ${LINE}" | tee -a leases.yml
    looper=0
  fi
done

The first rule is the shebang, it defines the interpreter that should be used to execute the script.

Next I define my variables and CONSTANTS. INFILES contains the content of the text file I just created. This text file will be passed as the first parameter. OUTFILE is the name of the file I will write the result to. This will be passed to the script as the second parameter. The looper variable will be used to count to 3 (0-2), as every lease uses 3 rules (mac, ip and name).

Then I write hosts: to initialize the output file. This will clear the file if it exists, or create a new one if it doesn't.

For writing to the output file, I use tee instead of a redirect (>). The tee command writes output to a file and to the standard output. If the -a flag is used with tee, it will append the text to file instead of overwriting it.

Next I will start a loop that goes over every line of the text file. I use if statements to check the current value of looper. If it is 0, I will append - mac: in front of the line. This is because we know the mac address will always be on the first line. The completed rule will be written to the bottom of the output file. We also increment looper so we know where we are. For the next line, looper will be 1. We write ip: in front of the line, append it to the output file an again increment looper. When we reach the next line, we add name: in front of it, and append it to the output file. Now we reset the looper back to 0 so we know the next line will start a new device.

If we now run the script we get this:

[jeroen@GHOST Scripting1]$ ./leases.sh ./leasesExample.txt ./leasesExample.yml
hosts:
  - mac: aa:aa:aa:aa:aa:aa
    ip: 10.0.1.111
    name: devicea
  - mac: aa:aa:aa:aa:aa:ab
    ip: 10.0.1.112
    name: deviceb
  - mac: aa:aa:aa:aa:aa:ac
    ip: 10.0.1.113
    name: devicec
  - mac: aa:aa:aa:aa:aa:ad
    ip: 10.0.1.114
    name: deviced

The generated yaml file is displayed on the screen and is written to the file you passed as the second parameter (leaseExample.yml in my case).

That's all, a simple script for a simple task. If you got any questions, don't hesitate to contact me.

When writing shell scripts, I highly recomend to use ShellCheck. It's a simple tool that checks your shell script on a lot of common issues and best practices.