1

I have this update script i am working on and i am using ssh to log onto the machine and simply run apt-get update && apt-get upgrade -y. Now the thing is i want to be able to say something like "hostname" updated successfully.

Here's what i have written right now:

#!/bin/bash

ip=(192.168.1.23 192.168.1.40 192.168.1.41 192.168.1.42 192.168.1.43)
#ssh-key=~/.ssh/id_ed25519


for i in "${ip[@]}"; do {
  ssh -t victor@$i "sudo apt-get update && sudo apt-get upgrade -y"
    if [ $? -eq 0 ]; then
      echo "Update went successfully";
    else
      echo "$(tput smul)$(tput setaf 1)Update failed$(tput rmul)";
      exit 1;
    fi
} done;

with the same ssh -t command i was thinking if i could get the hostname and assign it to a variable at the same time i'm using the first ssh session. That is so i dont have to write in my password for the ssh key twice.

So something like this:

hostname=$"(ssh -t victor@$i hostname && sudo apt-get update && sudo apt-get upgrade -y"

And then i just echo $hostname.

4
  • BTW, instead of if [ $? -eq 0 ], it's better just to put the ssh command into the if statement in-line: if ssh -t "victor<$i" "..."; then Commented Mar 31, 2017 at 14:11
  • BTW, another way to do this is with SSH multiplexing to send multiple separate commands over one authenticated transport. Commented Mar 31, 2017 at 14:24
  • Quick note -- feel free to add your own answer if you're so inclined, but answers shouldn't be included into the question itself, as they "privilege" that answer, making it above community review (prohibiting community voting on the answer distinct from voting on the question). Commented Mar 31, 2017 at 15:03
  • Another thing is that right now, you're checking the exit status of the command hostname=${hostname%$'\r'}, not the exit status of the ssh command (and if you just removed the hostname= line without making any other changes, you'd be checking the exit status of cat). Please adopt the edits in the current version of my answer if you want to detect failed apt-get commands reliably. Commented Mar 31, 2017 at 15:04

2 Answers 2

1
{ read -r hostname; read -r -d '' rest; retval=$?; } < <(
  ssh "victor@$i" "
    hostname || echo
    sudo apt-get update && sudo apt-get upgrade -y && printf '\0'
  "
)
printf '%s\n' "$rest"
if [[ $retval = 0 ]]; then  ## this is intentionally a string comparison, not numeric
  echo "Remote SSH command was successful"
else
  echo "Remote SSH command failed"
fi

Let's break this down into pieces:

  • <(...) is process substitution syntax, which substitutes a filename which will, when read from, return the output of a subshell running the given commands. Thus, < <(...) is redirecting from this process substitution.
  • read -r hostname, run locally, reads a single line into the variable named hostname.
  • read -r -d '' rest reads up to the next NUL character, with a successful exit status if such a NUL is seen, and an unsuccessful exit status if not.
  • hostname || echo writes the system's hostname (if successful) or an empty line (otherwise).
  • ... && printf '\0' puts a NUL on the end of the output from ssh (causing read -r -d '' rest to exit with a successful exit status) only if the commands in ... succeeded.

(Simplified from the original answer to no longer need bash 4.4 to detect when the remote apt-get commands failed).

Sign up to request clarification or add additional context in comments.

11 Comments

Hey thanks! That's super appreciated with the break down! However, it does not quite work, at least not the way i'm implementing it. I still cant echo $hostname since it's still empty. That is when the command completed successfully. I basically just replaced my "ssh -t victor@$i "sudo apt-get update && sudo apt-get upgrade -y" with the command you provided above.
The above will populate the local variable hostname if run exactly as I gave it.
...now, if you had a command substitution surrounding, that would cause the captured value to be lost. Could you paste the exact code and a transcript of running it with bash -x on gist.github.com? In an ideal world, PS4=':$LINENO:$BASHPID+' would be set (passed in on the environment in earlier versions of bash, or at the top of the file in 4.4), so we could verify that the read hostname and the place you echo "$hostname" are run in the same process..
(As an aside, I'd tend to suggest adjusting your remote /etc/sudoers to not require a TTY rather than using ssh -t -- less chance of apt-get engaging behavior that's only meant to be active in interactive use that way).
Here it is: gist.github.com/critaL/4a89f2cbcdecf42e10c05a2174794bab This is what my script looks like now. And yeah, i will fix that in the future i just want to get this working first :)
|
0

Maybe something like

hostname="$(nmblookup -A <ip>)"

and then

if [ $? -eq 0 ]; then
  echo "$hostname: Update went successfully."
else
  echo "$hostname: Update failed!"
fi

2 Comments

Thanks but nmblookup does not work in this case, i get no reply from the hosts. I tried disabling the firewall but it did not work.
nmblookup is very specific to Windows-esque environments. If you want to do a more generic reverse DNS lookup, that's something more like dig +short -x "$ip" -- though it's still not guaranteed to be a match for what the host thinks its name is, as reported by hostname.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.