Search code examples
bashshellvariableshtml-table

How to create a granular bash script with multiple variables with ssh connections


I have the below script:

Script:

#!/bin/bash
###########
printf "\n"
marker=$(printf "%0.s-" {1..60})
printf "|$marker|\n"
printf "|%-10s | %-13s | %-29s |\n" "Hostname" "RedHat Vesrion" "Perl Version"
printf "|$marker|\n"

remote_connect() {
   target_host=$1
   marker=$(printf "%0.s-" {1..60})
   rhelInfo=$(ssh -i /home/zabbix/.ssh/ssh_key "root@${target_host}" -o StrictHostKeyChecking=no -o PasswordAuthentication=no cat /etc/redhat-release| awk 'END{print $7}')
   perlInfo=$(ssh -i /home/zabbix/.ssh/ssh_key "root@${target_host}" -o StrictHostKeyChecking=no -o PasswordAuthentication=no "rpm -qa | grep -i mod_perl")
   if [[ $? -eq 0 ]]
   then
     printf "|%-10s | %-13s | %-20s |\n" "$target_host" "$rhelInfo" "$perlInfo"
   else
     printf "|%-10s | %-13s | %-20s |\n" "$target_host" "Unable to get the ssh connection"
fi
}  2>/dev/null
export -f remote_connect
< /home/zabbix/hostsList.txt  xargs -P30 -n1 -d'\n' bash -c 'remote_connect "$@"' --

The above script runs pretty well for me while running in parallel mode.

Script results:

|------------------------------------------------------------|
|Hostname   | RedHat Vesrion | Perl Version                  |
|------------------------------------------------------------|
|foxnl41    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl84    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl42    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl63    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl10    | 6.7           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl55    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl95    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |
|foxnl85    | 6.9           | mod_perl-2.0.4-11.el6_5.x86_64 |

Concern ?

I have two variables: rhelInfo and perlInfo to get store information. But it is using two ssh calls to the servers to get the values.

Could I have only one SSH call to execute multiple commands and set both variables?


Solution

  • Here is how I do remote data collection using ssh

    Some remarks

    • As this function is intended to collect datas, not to connect host (as they don't stay connected), I've renamed them ;-)

    • Avoid useless forks! use printf -v varname ... instead of varname=$(printf ...)

    • Moved STDERR redirection 2>/dev/null at more suited place.

    • use local to reserve variables names into function scope.

    • printf must have correct number of arguments, corresponding to number of %s see This format string has 3 variable....

    • Don't use -i switch to grep if not needed!

    remote_collect() {
        local target_host=$1 host_answer rhelInfo perlInfo
        {
            read -r host_answer
            read -r rhelInfo
            read -r perlInfo
        } < <(
            ssh -i /home/zabbix/.ssh/ssh_key "root@${target_host}" \
                -o StrictHostKeyChecking=no -o PasswordAuthentication=no \
                /bin/sh <<-EOF
            hostname
            awk 'END{print $7}' </etc/redhat-release
            rpm -qa | grep mod_perl
    EOF
        ) 2>/dev/null
        if [[ $? -eq 0 ]] ;then
            printf "| %-10s | %-14s | %-29s |\n" \
                "host_answer" "$rhelInfo" "$perlInfo"
        else
            printf "| %-10s | %-46s |\n" \
                "target_host" "Unable to get the ssh connection"
        fi
    }
    

    Notice: the EOF mark could be preceded by tabulations Tab, but not spaces! I've dropped tabulation before posting to ensure they won't be converted in space during cut'n paste.

    And the head line could be

    printf -v marker '%61s' ''
    marker=${marker// /-}
    printf "+%s+\n| %-10s | %-14s | %-29s |\n+%s+\n" \
       "$marker" "Hostname" "RedHat Version" "Perl Version" "$marker" 
    
    +-------------------------------------------------------------+
    | Hostname   | RedHat Version | Perl Version                  |
    +-------------------------------------------------------------+
    | hostok     | 6.3            | mod_perl-2.0.4-10.el6_5.armf  |
    | hostbad    | Unable to get the ssh connection               |
    

    Pretty table using UFT-8 borders

    printf -v sl %31s '';sl=${sl// /$'\U2500'}
    printf '%b%-12s%b%-16s%b%-31s%b\n' \
        \\U250c "${sl::12}" \\U252c "${sl::16}" \\U252c "$sl" \\U2510 \
        \\U2502 ' Hostname' \\U2502 ' RedHat Version' \\U2502 ' Perl Version' \
        \\U2502   \\U251c "${sl::12}" \\U253c "${sl::16}" \\U253c "$sl" \\U2524
    

    then:

        if [[ $? -eq 0 ]] ;then
            printf "\U2502 %-10s \U2502 %-14s \U2502 %-29s \U2502\n" \
                "$host_answer" "$rhelInfo" "$perlInfo"
        else
            printf "\U2502 %-10s \U2502 %-46s \U2502\n" \
                "$target_host" "      Unable to get the ssh connection"
        fi
    

    and finally

    # printf -v sl %31s '';sl=${sl// /$'\U2500'} # uncomment if $sl not at main scope
    printf '\U2514%s\U2534%s\U2534%s\U2518\n' "${sl::12}" "${sl::16}" "$sl"
    

    should produce:

    ┌────────────┬────────────────┬───────────────────────────────┐
    │ Hostname   │ RedHat Version │ Perl Version                  │
    ├────────────┼────────────────┼───────────────────────────────┤
    │ hostok     │ 6.3            │ mod_perl-2.0.4-10.el6_5.armf  │
    │ hostbad    │       Unable to get the ssh connection         │
    └────────────┴────────────────┴───────────────────────────────┘