How to Execute Remote Commands 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. 

SSH Interactive Mode

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.

How to Configure SSH for Non-Interactive Mode

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

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 dhcpd: DHCPREQUEST for 172.31.0.39 from b8:27:eb:.. via eth1
Aug 17 05:24:50 utilite dhcpd: DHCPACK on 172.31.0.39 to b8:27:eb:.. via eth1
Aug 17 05:24:52 utilite dhcpd: DHCPREQUEST for 172.31.0.38 from b8:27:eb:.. via eth1
Aug 17 05:24:52 utilite dhcpd: DHCPACK on 172.31.0.38 to b8:27:eb:.. via eth1
Aug 17 05:24:54 utilite dhcpd: DHCPREQUEST for 172.31.0.243 from b8:27:eb:.. via eth1
Aug 17 05:24:54 utilite dhcpd: DHCPACK on 172.31.0.243 to b8:27:eb:.. via eth1
Aug 17 05:24:55 utilite dhcpd: DHCPREQUEST for 172.31.0.183 from b8:27:eb:.. via eth1
Aug 17 05:24:55 utilite dhcpd: DHCPACK on 172.31.0.183 to b8:27:eb:dc:f9:83 via eth1
Aug 17 05:24:57 utilite dhcpd: DHCPREQUEST for 172.31.0.21 from b8:27:eb:.. via eth1
Aug 17 05:24:57 utilite dhcpd: DHCPACK on 172.31.0.21 to b8:27:eb:.. 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:.. 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      

Conclusion

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.

decoration image

Get your free trial now

Monitor your network from the user perspective

You can share

Twitter Linkedin Facebook

Let's keep in touch

decoration image