Linux for Network Engineers: How to Execute Commands Remotely with SSH

Secure Shell (ssh) is the standard way to connect to a remote machine’s shell. We routinely log in and use ssh to establish an interactive session and stay logged in as long as necessary. 

I often need to execute a single non-interactive command on a remote host such as to check a log file or a machine’s free disk space. Obviously, that can be accomplished by establishing an interactive session, run the command, and exit as follows:

netbeez.net$ ssh pi@172.31.0.16
Linux raspberrypi 4.19.57-v7+ #1244 SMP Thu Jul 4 18:45:25 BST 2019 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Aug 18 04:12:28 2021 from 172.29.0.13
pi@172.31.0.16:~:$ df -h   
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       7.3G  3.1G  3.9G  45% /
devtmpfs        459M     0  459M   0% /dev
tmpfs           464M     0  464M   0% /dev/shm
tmpfs           464M   18M  446M   4% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           464M     0  464M   0% /sys/fs/cgroup
/dev/mmcblk0p1   41M   23M   19M  55% /boot
tmpfs            93M     0   93M   0% /run/user/1000
pi@172.31.0.16:~:$ exit
logout
Connection to 172.31.0.16 closed.

Here is the step-by-step process:

  1. Logged into the remote machine
  2. Executed the command
  3. Logged out of the remote machine

Note that I had set up passwordless ssh beforehand to avoid typing in my password every single time.

This is not too bad, but when there is a better option why not use it?

The alternative is execute the command via ssh on the remote host as follows:

netbeez.net$ ssh pi@172.31.0.16 df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       7.3G  3.1G  3.9G  45% /
devtmpfs        459M     0  459M   0% /dev
tmpfs           464M     0  464M   0% /dev/shm
tmpfs           464M   18M  446M   4% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           464M     0  464M   0% /sys/fs/cgroup
/dev/mmcblk0p1   41M   23M   19M  55% /boot
tmpfs            93M     0   93M   0% /run/user/1000

test
As you can see I added the command df -h after the ssh login information, the command executed on the remote machine, and the output was printed on my local shell. This may not seem like a big deal, but I personally use it very often to get the IP address assigned by my DHCP server to a machine that just connected to the network as follows:

netbeez.net$ ssh netbeez@172.31.0.1 tail /var/log/syslog
Aug 17 05:24:50 utilite-desktop dhcpd: DHCPREQUEST for 172.31.0.39 from b8:27:eb:fd:1d:63 (nbagent-f0be) via eth1
Aug 17 05:24:50 utilite-desktop dhcpd: DHCPACK on 172.31.0.39 to b8:27:eb:fd:1d:63 (nbagent-f0be) via eth1
Aug 17 05:24:52 utilite-desktop dhcpd: DHCPREQUEST for 172.31.0.38 from b8:27:eb:18:61:62 (nbagent-649b) via eth1
Aug 17 05:24:52 utilite-desktop dhcpd: DHCPACK on 172.31.0.38 to b8:27:eb:18:61:62 (nbagent-649b) via eth1
Aug 17 05:24:54 utilite-desktop dhcpd: DHCPREQUEST for 172.31.0.243 from b8:27:eb:47:2f:c2 (nbagent-f0be) via eth1
Aug 17 05:24:54 utilite-desktop dhcpd: DHCPACK on 172.31.0.243 to b8:27:eb:47:2f:c2 (nbagent-f0be) via eth1
Aug 17 05:24:55 utilite-desktop dhcpd: DHCPREQUEST for 172.31.0.183 from b8:27:eb:dc:f9:83 (nbagent-f0be) via eth1
Aug 17 05:24:55 utilite-desktop dhcpd: DHCPACK on 172.31.0.183 to b8:27:eb:dc:f9:83 (nbagent-f0be) via eth1
Aug 17 05:24:57 utilite-desktop dhcpd: DHCPREQUEST for 172.31.0.21 from b8:27:eb:90:64:9b (nbagent-6162) via eth1
Aug 17 05:24:57 utilite-desktop dhcpd: DHCPACK on 172.31.0.21 to b8:27:eb:90:64:9b (nbagent-6162) via eth1

In this case, I know that the DHCP server logs in /var/log/syslog all IP assignments. With the remote execution of the tail /var/log/syslog command on the DHCP server at 172.31.0.1 I see that the last assigned IP was 172.31.0.21. I can also see the MAC address of the machine that this IP was assigned to was b8:27:eb:90:64:9b and from there I can identify the machine that got that IP.

The other benefit of remote execution of non-interactive commands with ssh is that you can include them in scripts since the output can be parsed like any other command output. For example, here is a script that checks if the root partition of a remote host is 90% full, and if it is it prompts to delete the 10 largest files in the /var/log/ directory:

REMOTE_HOST="pi@172.31.0.16"                                                                                                                                                                             
DISK_LIMIT=90                                                                                                                                                                                            
PARTITION="/"                                                                                                                                                                                            
CLEANUP_DIR="/var/log/"                                                                                                                                                                                  
if [ "$(ssh ${REMOTE_HOST} df ${PARTITION} | grep ${PARTITION} | awk '{print $5}' | tr -d %)" -gt "${DISK_LIMIT}" ]; then                                                                                
  echo "Remote host dist utilization greater than ${DISK_LIMIT}%"                                                                                                                                        
  echo "Cleaning up 10 largest log files"                                                                                                                                                                
  for file in $(ssh ${REMOTE_HOST} ls -S ${CLEANUP_DIR}/ | head); do                                                                                                                                     
      read -p  "Should I clean ${CLEANUP_DIR}/$(ls -la ${CLEANUP_DIR}/${file})?" -n 1 -r                                                                                                                 
      echo                                                                                                                                                                                               
      if [[ ${REPLY} =~ ^[Yy]$ ]]                                                                                                                                                                        
      then                                                                                                                                                                                               
          echo "Deleting ${REMOTE_HOST}:${CLEANUP_DIR}/${file}"                                                                                                                                          
          ssh ${REMOTE_HOST} rm ${CLEANUP_DIR}/${file}                                                                                                                                                   
      fi                                                                                                                                                                                                 
  done                                                                                                                                                                                                   
fi      

This is just a pointer to the remote command execution with ssh and just a few examples of how I use this as part of my daily Linux administration tasks. Drop me a line in the comments if you find any good use cases of this as well.