0

I want my command to exit with a status of failure (1), if the command returns no output in EOF. But I'm unable to use variable to store the command or store the output in a file.

For example:

#!/bin/bash
a='Some other commands in local machine'
ssh ubuntu@xx.xx.xx.xx << EOF
echo $a;
ldt=$(date +'%Y%m%d')
awk -v start="$(date +'%Y/%m/%d %H:%M' --date '-1000 min')" - F'[[:space:]]*[|][[:space:]]*' '
(\$4>=start) && /INFO: Server startup in/
' /some/file/path-\$ldt.log
EOF

Now, If the command gives an empty output, it should exit 1 and if some text is displayed, it should exit 0.

I'm unable to store the awk command in variable. If I store it in variable, the awk command doesn't work. Tried storing the output in the file but this also fails.

Please help me find a solution.

Ashutosh
  • 518
  • 7
  • 20
  • 1
    It "doesn't work" because of the unquoted heredoc causing expansions to happen **before** the remote code is run. Don't do that. – Charles Duffy Jul 22 '18 at 19:49
  • I assume that `ldt` should be "local" datetime? It wasn't, originally -- `date` was being run by the local machine, not the remote one. – Charles Duffy Jul 22 '18 at 19:53
  • Date on local machine or remote machine is same, so it will not matter @CharlesDuffy – Ashutosh Jul 22 '18 at 20:12
  • ...but it *would* matter if your date format had a space in it -- `ldt=$(date '+%Y%m%d %H%M%S')` is fine if run as a local command, but tries to run your `%H%M%S` results as a command with an environment variable `ldt` exported otherwise, because with the expansion run locally, the space is seen by the remote shell as syntactically significant, not as data. Which is to say that it's a difference worth internalizing and making a habit of thinking about. – Charles Duffy Jul 22 '18 at 20:22
  • @CharlesDuffy Yes, you're right, was my error. I'll be more careful. – Joaquin Jul 22 '18 at 22:27
  • @CharlesDuffy FYI I will be using this in Jenkins. And yes you are right. :) – Ashutosh Jul 22 '18 at 22:27

1 Answers1

2

When you use an unquoted heredoc -- <<EOF -- expansions therein are run before it's fed on stdin to the command being invoked.

This includes $(awk ...), making it difficult to correctly capture output from code generated on this way and operate on it later.

So -- one thing you can do is use a quoted heredoc, and then go back to the approach you tried earlier (capturing the awk results and branching on them), and it should work correctly.

The other thing you can do is have awk set its own exit status based on whether any matches are found.

Setting Exit Status In awk

#!/bin/bash
a='Some other commands in local machine'
printf -v args_q '%q ' "$a"

ssh ubuntu@xx.xx.xx.xx "bash -s $args_q" <<'EOF'
  a=$1
  echo "$a"
  ldt=$(date +'%Y%m%d')
  awk -v start="$(date +'%Y/%m/%d %H:%M' --date '-1000 min')" -F'[[:space:]]*[|][[:space:]]*' '
    BEGIN { found=0 }
    ($4>=start) && /INFO: Server startup in/ { print $0; found=1; }
    END { if (found == 0) { exit(1) } else { exit(0) } }
  ' "/some/file/path-$ldt.log"
EOF

Setting Exit Status After awk

#!/bin/bash
a='Some other commands in local machine'
printf -v args_q '%q ' "$a"

ssh ubuntu@xx.xx.xx.xx "bash -s $args_q" <<'EOF'
  a=$1
  echo "$a"
  ldt=$(date +'%Y%m%d')
  awk_result=$(
    awk -v start="$(date +'%Y/%m/%d %H:%M' --date '-1000 min')" -F'[[:space:]]*[|][[:space:]]*' '
      BEGIN { found=0 }
      ($4>=start) && /INFO: Server startup in/ { print $0; found=1; }
      END { if (found == 0) { exit(1) } else { exit(0) } }
    ' "/some/file/path-$ldt.log"
  )
  [[ $awk_result ]] && echo "$awk_result"
  [[ $awk_result ]] # success only if awk_result is nonempty
EOF

Note that this only works if <<EOF has been changed to <<'EOF', such that the awk command is evaluated remotely.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • What does %q do? Is it a part of syntax? Let me try this. – Ashutosh Jul 22 '18 at 20:14
  • 1
    `printf %q` emits a string in an `eval`-safe form. If you aren't testing with interesting enough strings, it might not *look* like it's doing anything, but it's critical for correctness in the face of unusual or malicious data. – Charles Duffy Jul 22 '18 at 20:16
  • Thanks for the useful Info! This works but doesn't print the output of the string if found. I tried the `Setting Exit Status After awk` Method. – Ashutosh Jul 22 '18 at 20:37
  • `echo "$awk_result"`, then. – Charles Duffy Jul 22 '18 at 20:38
  • This works like a charm! used the second method. Charles you are a life saviour. – Ashutosh Jul 22 '18 at 20:48
  • So, if I have multiple local variable, I will pass like this: `printf -v args_q '%q ' "$a" -v args_q '%q ' "$b";` and inside the loop `a=$1 b=$2`? I will be using array so this won't be an issue, right? Sorry for the silly question @CharlesDuffy. – Ashutosh Jul 22 '18 at 21:31
  • `printf -v args_q '%q ' "$a" "$b"`, passed with `"bash -s $args_q"`, and then `a=$1 b=$2`. – Charles Duffy Jul 22 '18 at 21:49
  • For array I'm using `printf -v args_q '%q ' "$a[@]" "$b[@]"`, passed with `"bash -s $args_q"`, and then `a[@]=$1 b[@]=$2`. This doesn't work! – Ashutosh Jul 22 '18 at 22:57
  • Please let me know how to use `array` and also `curl` command doesn't work in commented `here doc`. It only works in uncommented `here doc`. – Ashutosh Jul 23 '18 at 00:08
  • So, `printf -v args_q '%q ' "${a[@]}" "${b[@]}"` would put both arrays into the single `"$@"` list. There are other Q&A entries showing how to pass multiple arrays on a single command line (personally, I prefer the length-prefixed approach). – Charles Duffy Jul 23 '18 at 00:25
  • As for the claim that curl doesn't work, it's simply wrong -- curl works fine. More likely you're relying on expansions that require content only available from the local shell, but not actually passing that content across. Run `set -x` inside the heredoc to enable logging, and compare the working command line vs the broken one; you'll presumably see what the missing data is. – Charles Duffy Jul 23 '18 at 00:26
  • See the "legacy answer" form at https://stackoverflow.com/questions/10953833/passing-multiple-distinct-arrays-to-a-shell-function for one way to pass two arrays on one command line and parse them back out. – Charles Duffy Jul 23 '18 at 00:27
  • `curl -fIkSs https://xx.xx.xx.xx:9002 | head -n 1`, this is the curl command that I'm using. When I use it under commented `here doc`, it says `curl: (6) Could not resolve host:` and when I run it with uncommented `here doc`, it says `HTTP/1.1 200 OK` . – Ashutosh Jul 23 '18 at 00:36
  • Once again -- compare the `set -x` results. If the commands weren't different, the results wouldn't be either. – Charles Duffy Jul 23 '18 at 00:43
  • 1
    (If there's nothing after "Could not resolve host:", that implies that there was no hostname actually passed to `curl`; perhaps you're depending on a variable you didn't pass through the argument list?) – Charles Duffy Jul 23 '18 at 01:16
  • Yes, You are right! using commented `here doc` complicates things, for example arrays need to be combined, but in uncommented `here doc` arrays can be used directly. – Ashutosh Jul 23 '18 at 01:26