Copying variables to local text file from multiple ssh awk output

Viewed 208

I'm reasonably new to shell scripting, so I've had difficulty applying the answers to similar questions to my problem.

I am trying to ssh to a remote server, perform multiple awk commands, and return the value of each to a local .txt file. I am also trying to ssh to other servers, perform similar commands, and return them to the same text file.

If i manually ssh into the remote server and run df -h | awk '$6 == "/" {print $5; exit} I get the % used value for the root directory from the df -h command. Thus far, I have the following, but while it enters and exits the remote server it doesn't save the value.

> testfile.txt

ssh {$CURRENT_ENV} << EOF
VAL=\$(df -h | awk '\$6 == "/" {print \$5; exit}')
echo "\$VAL > testfile.txt"
exit
EOF

I've looked at single line awk returns, but as I have multiple commands to run it doesn't seem optimal. I would appreciate any suggestions!

3 Answers

First of all, your ssh command. Using a here-document is a good idea. You can improve it in two ways by:

  • indenting with TABs owing to the <<- syntax. This is purely cosmetic and makes your code more readable.
  • avoiding the escaping of special characters like $ by quoting EOF. This is not only cosmetic but makes your code less error-prone.

This gives:

ssh {$CURRENT_ENV} <<- 'EOF'
        VAL=$(df -h | awk '$6 == "/" {print $5; exit}')
        echo "$VAL > testfile.txt"
        exit
EOF

(we could even put a tabulation before EOF)

Now your code :

  • You don't tell us what CURRENT_ENV is. I assume this is something like user@server. To use that variable, write "$CURRENT_ENV", not '{CURRENT_ENV}'. Unless you know what you are doing, when using a variable, always enclose it in double-quotes to avoid any undesirable side-effect.
  • You put the result of df into variable VAL and write its content to textfile.txt:
    • As a universal convention, use lower cases for you variable names (unless they are exported to the environment which is not the case here); i.e. this should be val, not VAL.
    • echo "$val > testfile.txt" won't write anything into textfile.txt because your redirection is inside double-quotes, and thus belongs to the text that is echo-ed. Proper command would be echo "$val" > testfile.txt
    • Now, think about it: all this, including this echo is executed on the remote server, therefore this will create the file testfile.txt there, not on your machine. This is not what you want, so let's remove that echo line. Let's also remove val= since val is not needed any longer.
    • The exit command is of no need. Once the last command will be read and executed, the ssh session ends anyway.

We are left with this;

ssh "$CURRENT_ENV" <<- 'EOF'
        df -h | awk '$6 == "/" {print $5; exit}'
EOF

(remember this is a tabulation before df but single spaces wouldn't harm in this case)

As it is now, your code outputs everything to your terminal. Let's now redirect this to your local file testfile.txt :

ssh {$CURRENT_ENV} <<- 'EOF' > testfile.txt
        df -h | awk '$6 == "/" {print $5; exit}'
EOF

OK, this works for one server. You told us there were actually several ones. You don't show us your code, so I will assume there is a loop somewhere:

for ssh_target in u1@server1 u2@server2 ...; do
    ssh "$ssh_target" <<- 'EOF' > testfile.txt
        df -h | awk '$6 == "/" {print $5; exit}'
EOF
done

Almost there! The problem with this command is that each loop overwrites the content of testfile.txt. The solution is to redirect the for loop, NOT the ssh command inside it:

for ssh_target in u1@server1 u2@server2 ...; do
    ssh "$ssh_target" <<- 'EOF'
        df -h | awk '$6 == "/" {print $5; exit}'
EOF
done > testfile.txt

(the redirection must be put after done)

Here it is!

I think you can do this:

# Write all of this script's stdout to testfile.txt
> testfile.txt

# Run df -h on remote host, and pipe the stdout of df -h to awk
ssh "$host" df -h | awk '$6 == "/" {print $5; exit}'

Note that if you specify a filepath with df then you can get information for just that filepath. In your case you are only interested in /, so you can specify that.

If you are only interested in the percent used, you can use the --ouput=pcent option, and then use tail to get only the percentage part (leaving out the Use% header):

df --output=pcent / | tail -n 1

This will produce output with one or two leading spaces if the percent used is less than 100%. The leading spaces can be deleted with tr:

df --output=pcent / | tail -n 1 | tr -d ' '

If for some reason you want to avoid having two pipes, but you still want to remove leading spaces, you can use awk:

df --output=pcent / | awk 'NR == 2 {print $1}'

However using awk might be slower, I'm not sure.

If you want to print anything to local file, just do redirect immediately after EOF here is example how to write VAL to remotefile and to localfile. Also you can use df --output pcent to show only percentage column. Unfortunately there is no option to hide header, so tail -n1

ssh {$CURRENT_ENV} << EOF >>localfile.txt
VAL=\$(df --output=pcent /|tail -n1)
echo "$VAL" > remotefile.txt
echo "$VAL" # for localfile
exit
EOF
Related