Home Life Legos Tech Mac Graveyard
magnusviri

2014-04-18

Using heredocs in Apple Remote Desktop to run Perl/Python/Ruby code

The Send UNIX Command in Apple Remote Desktop

Apple Remote Desktop (ARD) is pretty cool. It has this Send UNIX Command ability that lets me send some BASH code to any number of machines. The only thing that I've come across that is as nice as this is Ansible.

I do have one gripe about ARD. The Send UNIX Command defaults to BASH on the clients and I don't believe that can be changed. I prefer to code in Perl so I finally figured out how to run Perl.

First, here is a little bit about the environment I when running these commands as root.

echo $0
pwd
echo $HOME
whoami

I get this.

/bin/bash
/
/var/root
root

Here document

A Heredoc is code that appears in a script and is treated as if it were a separate file. You can find out a lot more by reading Wikipedia's here document page.

In BASH the syntax looks like this (this doesn't actually do anything and will generate an error because some_command isn't real).

# line 1
# line 2
some_command << EOF
# separate file line 1
# separate file line 2
EOF
# line 7

Everything between "<< EOF" and "EOF" is treated as a different file (or a single string) and it's sent to some_command. The "EOF" can be any text and is used to mark the end of the heredoc. Here's another example and this one actually works.

cat << END_OF_FILE
line 1
line 2
END_OF_FILE

This works because cat expects a file. If I had used echo instead of cat it wouldn't have worked because echo prints the arguments sent to it, not a file where cat prints a file. This is important because when the perl command expects a file to execute and perl -e executes the first argument. So I can't use -e (when I first tried doing this I kept using -e and it never worked... duh).

Here is a simple BASH script that executes a perl script and gives it some code that prints "12". I can send this in ARD and it works.

perl << EOF
for ( 1..2 ) {
    print;
}
EOF

Escaping heredocs

So the thing that sucks is that this is still a BASH script, and as such BASH is treating this text as a string and so any occurrence of $ is treated as a variable. So this code will not work.

perl << EOF
$var = 1;  # BROKE!
EOF

It needs to be escaped like this.

perl << EOF
\$var = 1;  # Works
EOF

Using back ticks similarly doesn't work unless I escape them. Yes, this sucks.

perl << EOF
\$var = \`hostname\`
EOF

ARD Heredoc running Perl

Here is a more complex heredoc and gets me closer to what I want to do (different actions based on the hostname).

perl << EOF
use Sys::Hostname;
my \$hostname = hostname;
for ( \$hostname ) {
    /^radmind-1/ && do {
        print "Hi I'm 1";
        last;
    };
    /^radmind-2/ && do {
        print "Hi I'm 2";
        last;
    };
    /^radmind-3/ && do {
        print "Hi I'm 3";
        last;
    };
    do {
        print "Hi I'm \$hostname";
    };
}
EOF

ARD Heredoc running Python

It's also possible to run Ruby or Python code this way. Here's a Python example. Because Python doesn't use $'s for variables this is clearly the cleanest because there aren't any escape slashes. However, Python doesn't have a switch statement so I'm using elif's.

python << EOF
import socket
hostname = socket.gethostname()
if hostname[:9] == "radmind-1":
    print "hi I'm 1"
elif hostname[:9] == "radmind-2":
    print "hi I'm 2"
elif hostname[:9] == "radmind-3":
    print "hi I'm 3"
else:
    print "I'm " + hostname
EOF

ARD Heredoc running Ruby

Here's a Ruby example

ruby << EOF
require "socket"
\$hostname = Socket.gethostname
case \$hostname
when /^radmind-1/
  puts "Hi I'm 1"
when /^radmind-2/
  puts "Hi I'm 1"
when /^radmind-3/
  puts "Hi I'm 3"
else
  puts "I'm " + \$hostname
end
EOF

Conclusion

Welp, that's how to use heredocs in ARD. I'm so much more fluent in Perl then BASH so it was worth it to me. But the unbelievable irony here is that the problem I was trying to solve turned out to be actually easier in BASH. I didn't realize this until after I started coding the Perl version. Here's the final BASH script I wrote (which probably means nothing to you but oh well).

cd /Volumes/rserver/var/radmind/cert/
hostname=`hostname -s`
ln -s $hostname.pem link.pem
ls -l

Sigh.

Copyright 2017 James Reynolds