3

I am trying to redirect the bash output to a variable file name. Here is my script looks like

#!/bin/bash
for i in `cat servers`
do
if [ "$i" = "198.162.1.3" ];
then
var="apple"
fi
ssh test@$i "uname -n"
done > /tempout/uname_${var}.txt

I am getting the filename as /tempout/uname_.txt

Expected filename should be uname_apple.txt

  • Don't read lines with for – glenn jackman Sep 7 '18 at 12:47
  • @glennjackman What makes you think "servers" is a file of lines/records and not just whitespace separated IP addresses? – ColleenV Sep 7 '18 at 13:54
  • Even if it is, the for i in $(cat file) is wrong. The shell will not only do word splitting (which is the desired effect), it also does filename expansion. set -f is missing. – glenn jackman Sep 7 '18 at 14:27
7

The var variable in your code is used before the loop starts to create the output file.

If you want to output the result of the ssh command to a file whose name you construct from $var, then do this:

#!/bin/bash

while read -r server; do
    if [ "$server" = "198.162.1.3" ]; then
        var='apple'
    else
        var='unknown'
    fi

    ssh -n "test@$server" 'uname -n' >"/tempout/uname_$var.txt"
done <servers

Here, I've also changed the loop so that it reads the input file line by line (ignoring leading and trailing whitespace on each line), and I've made var get the value unknown if the if statement does not take the "true" branch.

Also, you need -n for ssh. Otherwise, ssh would consume all the available input (here redirected from the servers file).

Another change that could be made is to use case ... esac rather than an if statement, especially if the number of IP addresses that you test for is more than a couple:

#!/bin/bash

while read -r server; do
    case $server in
        198.162.1.3) var=apple ;;
        198.162.1.5) var=cottage ;;
        198.162.1.7) var=bumblebee ;;
        *) var=unknown
    esac

    ssh -n "test@$server" 'uname -n' >"/tempout/uname_$var.txt"
done <servers
  • 1
    the input redirection to read is more elegant, but the loop behaves fundamentally different like that. if IP appears in a line together with other words it won't be recognized. – Sam Sep 7 '18 at 10:12
  • @Sam Yes, the new code expects servers to be a list of IP addresses, one per line. – Kusalananda Sep 7 '18 at 10:32
  • one per line, with leading and trailing spaces or tabs removed (assuming the default value of $IFS) – Stéphane Chazelas Sep 7 '18 at 11:07
  • @StéphaneChazelas: I’m trying to understand your comment. It looks like it’s intended as a correction to Kusalananda’s comment, and, IMHO, it doesn’t work as such. Arguably, Kusalananda’s comment should say “the new code expects servers to be a list of IP addresses, one per line, with optional leading and trailing spaces or tabs (which will be removed / ignored).” – G-Man Sep 14 '18 at 19:34
  • 1
    @G-Man Stéphanes' comment was a clarification to my text, not a correction (that's how I read it at least). He is absolutely correct, but just like with your comment regarding my use of read -r, it's unlikely to make a big difference here as IP addresses rarely include leading or trailing whitespace. I would matter if IFS has a non-default value (like a collection of digits), in which case using IFS= (no value) in front of read becomes important. – Kusalananda Sep 14 '18 at 20:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.