Running Commands on Another Computer

ssh host 'command' runs one command on another computer and brings back its output and its exit code, the kind of quotes decides which computer fills in a $variable, and a short deploy script copies a site with rsync and checks it with ssh in one step.

Freelancer · Unix · Linux · Updated

After this lesson you can

  • Run a command on another computer with ssh host 'command', choosing single or double quotes for where a $variable is filled in
  • Write a deploy script that copies a site with rsync and then runs a command on the server with ssh

Key takeaways

  • ssh host 'command' runs one command there and comes straight back. Its output prints here, and its exit code becomes ssh's, so && and if work.
  • In single quotes, a $variable reaches the other computer as written and is filled in there. In double quotes, your computer fills it in before ssh sends it.
  • A pipe outside the quotes runs on your computer; inside them, it runs on the server.
  • A deploy script copies with rsync -av --delete and checks the result with ssh, the same way every time, in one command.

Is the new page on the server? How full is its disk? Logging in, typing one command and logging out again is three steps for one answer, and a script can't log in and type at all. Give ssh a command after the computer's name, and it runs that one command there and comes straight back. Put that together with rsync, and you have a deploy script: the one command a web developer runs to put a site live, the same way every time, without forgetting a step.

The examples use the book club's site from Copying Between Computers: scp and rsync, and its short name bookclub. Start in its folder: cd ~/terminal-practice/book-club. The server is your own computer again, so the output shown in this lesson is what these commands print with bookclub pointing at localhost.

One command, then home: ssh host 'command'

Put the command in quotes after the host:

ssh bookclub 'ls bookclub-site'
images
index.html
meetings.html
reading-list.txt

That's the server's ls, printed in your terminal. It lists one name a line, because its output goes back through ssh instead of to a screen. This is a remote command: no prompt, no exit; ssh ends when the command does. The command's exit code comes back too:

ssh bookclub 'ls bookclub-sit'; echo "exit code: $?"
ls: cannot access 'bookclub-sit': No such file or directory
exit code: 2

So &&, || and if work on a remote command as they do on your own (Did It Work? Exit Codes, && and ||). Pipes do too, and where you put one decides which computer does the work:

ssh bookclub 'cat bookclub-site/reading-list.txt' | sort
Frankenstein
Little Women
The Hobbit

Here sort ran on your computer. Inside the quotes, ssh bookclub 'sort bookclub-site/reading-list.txt', the server sorts. The answer is the same; for a huge log file, filter on the server, so less has to travel.

Whose $variable? The quotes decide

Your own shell reads the whole line first, quotes and all, before ssh sends anything (Quotes: Double, Single and None). In double quotes, your shell fills in a $variable from your computer. In single quotes, it leaves the $ alone, and the server's shell fills it in there. Put echo in front to see exactly what ssh would be given:

echo ssh bookclub "ls $PWD"
echo ssh bookclub 'ls $PWD'
ssh bookclub ls /home/sam/terminal-practice/book-club
ssh bookclub ls $PWD

The first asks the server for a folder that's on your laptop. The second asks for the folder ssh starts in over there, your home folder on the server. The rule: single quotes for the server's own values, double quotes for values only your computer knows, such as a script's variables. With localhost both are the same computer, so the mistake hides while you practice; on a real server it won't.

A deploy in one script

The book club replaces its reading list with a books page:

rm site/reading-list.txt
printf '<h1>Books</h1>\n<p>The Hobbit, Little Women, Frankenstein</p>\n' > site/books.html

Make deploy.sh in the book-club folder with nano (Your First Script):

#!/usr/bin/env bash
# deploy.sh: puts the book club's site on its server.
set -euo pipefail

server=bookclub
folder=bookclub-site

rsync -av --delete site/ "$server:$folder/"
echo "Live on $server:"
ssh "$server" "ls $folder"
  • set -euo pipefail stops the script at the first failure (Strict Mode: set -euo pipefail), so a failed copy is never followed by "Live".
  • --delete removes files on the server that are gone from site/, so the server's copy matches yours exactly. It also means an empty site/ empties the live site, so when in doubt, run the rsync line with -n first.
  • "ls $folder" is in double quotes on purpose: $folder is the script's variable, on your computer, so your shell fills it in before ssh sends it.

Make it runnable and deploy:

chmod +x deploy.sh
./deploy.sh
sending incremental file list
deleting reading-list.txt
./
books.html

sent 304 bytes  received 66 bytes  740.00 bytes/sec
total size is 220  speedup is 0.59
Live on bookclub:
books.html
images
index.html
meetings.html

books.html went up, reading-list.txt came down, and the server says what's live. Run it again with nothing changed, and rsync sends nothing. One command, every step, every time.

Check yourself

2 questions to try on the lesson page.

So what

ssh host 'command' lets a script do anything on a server that you could do logged in, and a deploy script turns "put the site live" from a checklist you might get wrong into one command you can't.

Key terms

Cheat sheets

Related