3

I have a bash script that is reading a CSV file , that contains source IP address , destination IP address and destination port.

My script is basically doing 2 tests:

1 Performance 2 Connectivity.

Under performance test, I am copying files from source to destination (using scp) to calculate the the network IO. After that, copying the same file from one location of Destination server to other location on destination server to calculate Disk I/O.

Under connectivity test I am doing nc from source to destination to check whether port is open or not.

Problem : Sometime while execution of script, it get stuck at some point and then I have to press Ctrl+C to kill that process to continue the further execution.

Here , i am not able to figure it out why it is getting stuck, and even behavior is not consistent , every time it's stuck at different location in the script.

Please help me in resolving this issue. If required I can post my complete script here.

Update :

Observation : Other thing that i observed , if i don't press Ctrl+C , then script resume automatically after 5 mins.

Part of the actual script , where it is getting stuck.

#Connectivity Test
TMP=$(mktemp)
nc -z -v -n $lastDestinationIP $port >\$TMP
if grep -q "succeeded" <<< cat echo \$TMP;then
   echo $x','$lastSourceIP','$lastDestinationIP','$sourceFqdn','$fqdn','$port',Connectivity,NA,NA,NA,Pass,'$(date) | ssh $username@$baseLocation 'cat >> report.txt'
else
  echo $x','$lastSourceIP','$lastDestinationIP','$sourceFqdn','$fqdn','$port',Connectivity,NA,NA,NA,Fail,'$(date) | ssh $username@$baseLocation 'cat >> report.txt'
fi
rm $TMP
fi
exit
ENDSSH

Most of the time it stuck while calling exit.

Here is the snippet where it stuck most of the time (from the console).

21 14:48:16 EDT 2014 | ssh puppet-admin@10.X.X.9 'cat >> report.txt','Fri Mar
> fi
>
> #rm KB_33.txt
> #rm MB_10.txt
> #rm MB_100.txt
> rm -rf dummy
> else
> #Connectivity Test
> nc -z -v -n -w 2 10.X.X.17 1524 >/tmp/tmp.GJ1knZF5Jn
> if grep -q "succeeded" <<< cat echo /tmp/tmp.GJ1knZF5Jn;then
 21 14:48:16 EDT 2014 | ssh puppet-admin@10.X.X.9 'cat >> report.txt','Fri Mar
> else
21 14:48:16 EDT 2014 | ssh puppet-admin@10.X.X.9 'cat >> report.Fail,'Fri Mar
> fi
> fi
> exit
>
saurav
  • 239
  • 1
    You can start your script in debug mode by invoking the -x of the shell (valid for bash, I don't know for other shells) : bash -x <script name>. You will see more details over what the script is doing when it gets stuck. – Benoit Mar 21 '14 at 18:39
  • i tried that but it is not helping much – saurav Mar 21 '14 at 18:44
  • I don't think the code you pasted is correct at all. Could you please post relevan parts of the script. Also expected output and actual output would help. – GnP Mar 21 '14 at 19:19
  • Rather than kill it you can also use strace to connect to the running process ID to take a peek inside to see what it's blocking on. strace -p <PID> – slm Mar 21 '14 at 19:20
  • So '21 14:48:16 EDT 2014' is supposed to be echoed across the |ssh pipe to cat >>? What sends it there, in your if/else? – mikeserv Mar 21 '14 at 20:14
  • You need to fix your quotes. When date expands it breaks apart the "echo". Also you dont need all of that stuff: cat <>~/my.file\n $Var1 $Var2 $Var3\nHEREDOC\n ; cat ~/my.file - values of Var1 Var2 Var3 – mikeserv Mar 21 '14 at 20:18
  • Youd be better off feeding your ssh commands a separate heredoc a piece and you wouldnt wind up catting for 5 minutes because you wouldnt need so many craxy quotes. – mikeserv Mar 21 '14 at 20:20
  • @mikeserv in the if else I am sending the status either as "Pass" or "Fail". I didn't understand last 2 comments of yours , can you please put more light on this. I am just a newbie and have written the script for the first time. Please bear with me. – saurav Mar 21 '14 at 20:39
  • Ive written this answer before - im trying to find it now... – mikeserv Mar 21 '14 at 20:40

1 Answers1

4

Here's the short and sweet - a heredoc is basically a file streamed to a file-descriptor.

Most people don't denote the 0<<descriptor and so you get it on <&0 stdin. ssh passes stdin to its invoked process so if you feed it a heredoc it will pass through the input to the invoked remote shell.

The one very special quality about heredocs is the difference between a \"'quoted and unquoted heredoc LIMITER. So, <<'THIS' differs from <<THIS. When you do not quote the LIMITER, the contents of the here-document are evaluated for ${shell:+expansion}. Once one ${shell:+expansion} pass is completed, there is very little else to distinguish a here-document from any other file fed as <~/input.

For example:

cat <<\QUOTED >~/file
    $(echo "This is ${NOT:-} expanded.")
#END
QUOTED
cat <~/file
> $(echo "This is ${NOT:-} expanded.")
> #END

But...

cat <<UNQUOTED >~/file
    $(echo "This is ${NOT:-} expanded.")
#END
UNQUOTED
cat <~/file
> This is  expanded.
> #END

You keep using the bash <<< herestring with cat. I don't know exactly how the herestring works but I'm willing to bet cat's already involved. So cat concatenates its <&0stdin with its stdout>&1. That's all it does. So you're unnecessarily complicating <<STDIN when you <<< cat it.

This can be a real problem if cat winds up consuming an input stream which you did not intend it to consume. Run just % cat at your terminal and it will look like nothing is happening because a terminal's stdin and stdout are the same file - your $(tty). But when they differ, cat combines them anyway and that can get pretty messy if you didn't mean it to happen.

It looks to me like some \'quotes are skipping an expansion when $(date) is $expanded. Then possibly the : null shell builtin is invoked and |piped to the next unquoted command after ssh which would be cat >> report.fail which should generate nothing at all in that file. So cat is >>appending /dev/null to report.fail, for as long as it can stand it, I guess. Or, more likely, for as long as exit allows ssh to carry-on proxying the null stream.

Also, have you checked to see if you've got a literal $TMP in your current working directory? I do see ENDSSH at the bottom which looks like a heredoc LIMITER to me so I believe either this is not the entire script or it has been edited by mistake. It would make sense if it were the body of a heredoc to use \$TMP, but as is I think nc will first >truncate then write its stdout to a file named $TMP. Then again, I guess you rm it anyway, so maybe you just didn't notice.

And because you're rming $TMP you may not have realized anyone would be asking this question:

How does $(mktemp) work for you without the filename.xxx argument?

UPDATE I looked closer at your output and definitely >/tmp/tmp.GJ1knZF5Jn means $(mktemp) is working - even the \$TMP part. So you've just taught me that I only need to specify mktemp .xxx if I specify a filename at all. Thank you.

Still I think there is a heredoc at the top somewhere? It could be there is not and \\ is only an attempt to deal with a side-effect of the echo \$TMP <<<herestring, but I dunno... Interesting.

I don't know if I've got this completely right because I don't know where all of these variables come from. But, this is close to how I would do this:

(which actually renders the last two questions irrelevant anyway)

_ssh() ( ssh "$1"@"$2" 'printf %s, `cat` >> '"$3"
) <<-PARAMS   
    "$lastSourceIP" 
    "$lastDestinationIP" 
    "$sourceFqdn" 
    "$fqdn" 
    "$port" "
    "ConnectivityNA" 
    "NA" 
    "NA" 
    "Pas" 
    "$(date)"
PARAMS

nc -z -v -n $lastDestinationIP $port |\
    grep -q "succeeded" && suffix=txt
_ssh user host report.${suffix:-fail}
unset suffix
?ENDSSH?

Note: the "$quotes" in the above are for printf's benefit on the other side of the ssh process - not for anything else. Those "quotes" remain as is even after all of the above PARAMS are evaluated.

There are some things going on up there that I've covered before. For instance the func() ( scope ) I like to think was covered ok here. The ${parameter:-expansion} was also covered there, but also demonstrated pretty well here and here. I've gotten into some of the weird heredoc stuff here, here, and here. Probably there are others - I guess I like messing up my shell or something.

In this case, though, using the function and the heredoc as I have, cat can't get stuck. PARAMS is sent over as stdin and cat will quit when it reaches an EOF (or CTRL-D) so as soon as it consumes PARAMS it's going to stop everytime. This is especially important if you are running this in a heredoc which is also on <&0 because PARAMS will stand in the way of cat eating your script mid-execute.

Anyway, hopefully that helps. If I've missed anything, don't hesitate to ask.

mikeserv
  • 58,310
  • Thanks, I don't totally follow all this but wanted to mention that there is a weird difference between "cat | tee output.txt <<EOF" and "cat <<EOF | tee output.txt". Use the latter format in scripts because "cat | tee" can hang waiting for an Enter on stdin. – jamshid Apr 04 '17 at 20:09