Check if certain files exist on remote servers

Shell: Bash in Oracle Linux 8.9

On around 100 Linux (Oracle Linux) servers, I need to check if the following files exist.

/opt/appfin/base/admin/file_abc.conf
/opt/appfin/base/admin/file_qrs.conf
/opt/appfin/base/admin/file_xyz.conf

If any of the above mentioned file exists in a server, then print nothing ie. do nothing.
But, if the file does not exist, then print something like 'file_abc.conf does not exist on server 10.87.16.178'

Courtesy of MadeInGermany from another thread, I came up with a basic WHILE loop like below (in code snippet)

On a Linux jump server, I will place all servernames (or IPs) in a file named hostlist and pass it to a WHILE loop like below.

It is work in progress and untested.

I have two questions:

  1. It has one IF..ELSE statement for each file I am checking for existence. Or can all files be checked for existence in one IF..ELSE statement ?

  2. What is logic to do nothing inside a IF statement in bash shell ?

The basic WIP WHILE loop, I have created

while read -r server junk <&3
do 
    echo -e "Currently executing commands on $server ....\n\n"
    ssh -n "oracle@$server" "
    FILE1=/opt/appfin/base/admin/file_abc.conf
    FILE2=/opt/appfin/base/admin/file_qrs.conf
    FILE3=/opt/appfin/base/admin/file_xyz.conf

    if [ -f "$FILE1" ]; then
        <logic to do nothing in bash shell>
    else 
        echo -e "file_abc.conf does not exist on $server......"
    fi
    if [ -f "$FILE2" ]; then
        <logic to do nothing in bash shell>
    else 
        echo -e "file_qrs.conf does not exist on $server......"
    fi
    if [ -f "$FILE3" ]; then
        <logic to do nothing in bash shell>
    else 
        echo -e "file_xyz.conf does not exist on $server......"
    fi
    "
done 3< hostlist

@james79 , one potential (there's any number of ways ...)

FILES[1]=file_abc.conf
FILES[2]=file_qrs.conf
FILES[3]=file_xyz.conf
for idx in "${!FILES[@]}"
do
        [ ! -e "${FILES[idx]}" ] && echo "${FILES[idx]} does not exist"
done

as always, test and go with what works for you ...

1 Like

The following seems to have worked. I had add in an extra check for the existence of /opt/appfin/product/version5 directory.
The files need to checked for existence only if /opt/appfin/product/version5 directory exists in that server.

while read -r server junk <&3
do 
    echo -e "Currently executing commands on $server ....\n\n"
    ssh -n "oracle@$server" "
    if [ -d /opt/appfin/product/version5 ]
    then   
        if [ ! -f "/opt/appfin/base/admin/file_abc.conf" ]; then
            echo -e "file_abc.conf does not exist on $server......"
        fi
        if [ ! -f "/opt/appfin/base/admin/file_qrs.conf" ]; then
            echo -e "file_qrs.conf does not exist on $server......"
        fi
        if [ ! -f "/opt/appfin/base/admin/file_xyz.conf" ]; then
            echo -e "file_xyz.conf does not exist on $server......"
        fi
    else
    echo "Skipping...........EBFin is not installed on this server: $server ........."
    fi
    "
done 3< hostlist

why not simply embed the for ... within the test for /opt/appfin/product/version5

    if [ -d /opt/appfin/product/version5 ]
    then
          FILES[1]=/opt/appfin/base/admin/file_abc.conf
          FILES[2]=/opt/appfin/base/admin/file_qrs.conf
          FILES[3]=/opt/appfin/base/admin/file_xyz.conf
          for idx in "${!FILES[@]}"
          do
               [ ! -e "${FILES[idx]}" ] && echo "${FILES[idx]} does not exist"
          done
    else
1 Like

A remote script in " " ??
I'd go for the safe method: remote script as a quoted here document.

while read -r server junk <&3
do 
    echo -e "Currently executing commands on $server ....\n\n"
    ssh "oracle@$server" /bin/bash -s "$server" << "_EORS_"
#!/bin/bash
# Positional parameters
server=$1
# Other variables
FILE1=/opt/appfin/base/admin/file_abc.conf
FILE2=/opt/appfin/base/admin/file_qrs.conf
FILE3=/opt/appfin/base/admin/file_xyz.conf
if [ ! -d /opt/appfin/product/version5 ]
then
    echo "Skipping...........EBFin is not installed on this server: $server ........."
    exit
fi
for fn in "$FILE1" "$FILE2" "$FILE3"
do
    if [ -f "$fn" ]; then
        : ok, do nothing
    else 
        echo -e "$fn does not exist on $server......"
    fi
done
# EndOfRemoteScript
_EORS_
done 3< hostlist

ssh without -n because the embedded script is passed through ssh to the remote /bin/bash

1 Like

Thank You MadeInGermany, MunkeHoller.

@MIG
You said

A remote script in " " ??

So, is it not very safe to run commands in remote server like below ? Commands enclosed withing " " ?

while read -r server junk <&3
do 
    echo -e "Currently executing commands on $server ....\n\n"
    ssh -n "oracle@$server" "
    commmand1
    command2
    .
    .
    commandn
    "
done 3< hostlist

The issue is that quotes do not nest. If your commands list does not include any quotes at all, it should (possibly) work.

However, in you original post, about the 9th line, the "$FILE1" is not quotes within quotes. The first " pairs with the last quote on the ssh line, and the last " resumes the quoting. That has two effects:

(a) The outer shell uses up all the quotes in the script sent to the server. This will probably fail, or have side effects, for expansions that result in spaces in the text.

(b) All the shell expansions in the script sent to the server are made in the scope of the local shell, not the remote one.

It is possible to alternate single and double quotes in the ssh part of the script to control the treatment of quoting and variable scope, but it can get very messy, HereDocs essentially have a third way of quoting which bypasses the quotes quandary.

Another wild feature of unquoted(!) HereDocs is that you can insert bash snippets inside them. This is a small section from a 100-line HereDoc which creates a macro list which later expands into a complete schema for a major project. This particular snippet expands into ten years worth of schema for monthly tables named like LA_RESULTS_202403, and a View over them all.

#.. LA_RESULTS
#.. For a view, the keys_list is an RE matching the tables composing the view.
_table la_results ${LOCN} 100 view ^la_results_20[0-9][0-9][0-9][0-9]$

#.. LA_RESULTS_YYYYMM
$( for LA_YM in ${LA_RESULTS_YYYYMM}; do
    echo "_table ${LA_YM} ${LOCN} 500000000 btree ymdhh, plant_reference, plant_type"
    echo "_cols  ymdhh i4 plant_reference c11 plant_type c4 kw f4 kvar f4 quality f4"
done )
1 Like

You can use that as long your commands are simple, have no special characters $ \ " `

1 Like