Home > Enterprise >  Bash getopts to remotely run the command via SSH
Bash getopts to remotely run the command via SSH

Time:03-30

I am trying to write a bash script which will take the input in form of arguments and execute certain commands (provided as arguments) on the remote hosts which will be provided via txt file

For eg: there is text file /var/tmp/hosts.txt which have hostnames which we will be executing a set of commands. When I run the script (command.sh) I should be able to run commands on the remote hosts provided in the hosts.txt file

For instance: Hosts file - hosts.txt and Command to be executed is:

lscpu  | grep "Model name" | cut -d: -f2 | awk '{$1=$1};1'

I should be able to execute in this way:

command.sh -f hosts.txt -c "lscpu  | grep "Model name" | cut -d: -f2 | awk '{$1=$1};1'"

and it should run the command provided in the -c argument on hosts xyz after doing an ssh

I was able to get single line commands working (whoami, hostname date .. ) but when it comes to combination of commands, I failed to achieve that because it takes them as different set of arguments.

\#!/bin/bash

usage () {
    echo "Usage: $0 \<Hostfile\>"
    exit -1
}

id="rm08397"

while getopts ":c:f:" option; do

    case "$option" in
        f) file_arg=$OPTARG ;;
        c) command_arg="$OPTARG" ;;
    esac
done

if \[ "$file_arg" != "" \]; then
    HOSTFILE="$2"
fi

if \[ $command_arg != "" \]; then
    cmd="$command_arg"
else
    cmd=$4
fi

for host in `cat $HOSTFILE`
do
    echo -n "$host#"
    ssh $id@$host $cmd
done

CodePudding user response:

There are many ways to try to do this, but it's tough because of word splitting and quoting.

I would recommend running ssh like this:

ssh $id@$host /bin/bash -c "$cmd"

This let's remote bash take care of parsing the command.

In the comment below you are saying that it is not capturing the whole command after -c. The problem is not with the script but with how you invoke it. You use double quotes for the argument to -c, but within that argument you use more double quotes. Those cause the parser to end the command early. If you print out the command variable in the script you will see where it stops. To fix this, just put backslashes before any double quotes in the command string, or use single quotes there instead.

command.sh -f hosts.txt -c "lscpu | grep \"Model name\" | cut -d: -f2 | awk '{$1=$1};1'"

CodePudding user response:

Transfering work to the remote via ssh is uterrly hard. Do not care about proper double-quoting. Properly serialize data nad code.

Create a function to run the remote commands.

func() {  ....; }

Then reformat your program to properly serialize the function to the remote and run it.

...
-c) command_arg=$OPTARG; ;;
...
while IFS= read -r host; do
    echo -n "$host#"
    # magic
    # run bash -c on the remote side
    # with `declare -f <the_function>` serialization of the function
    # followed by `; <the_function>` actually exeucting the function
    ssh "$id@$host" "$(printf "%q " bash -c "$(declare -f "$command_arg"); $command_arg")"
done <"$hostfile"

Then you can do:

func() { lscpu  | grep "Model name" | cut -d: -f2 | awk '{$1=$1};1'; }
export -f func
./command.sh -f hosts -c func

Note that you do not have to care about properly \"\" double quoting your command. Remember to check your script with shellcheck. Do not use backticks.

  • Related