CertGrid CertGrid
Hands-on Lab·Red Hat Certified System Administrator

SSH, scp and rsync Transfers

Two objectives: accessing remote systems over SSH, and transferring files securely. Both are straightforward until quoting and trailing slashes get involved, and both of those are decided here by running the two forms side by side against a real second machine.

Essential Tools Guide 10 of 67 Beginner

Written against the versions above. `scp` on RHEL 10 uses the SFTP protocol underneath rather than the legacy scp protocol. Behaviour for the commands here is unchanged; the difference shows up only in exotic cases like remote glob expansion.

Commands run on RHCSA-A01 and reach RHCSA-B01. Any second machine you can ssh to works.
Server NameIP AddressOSRolesCPURAMHDD
RHCSA-A01192.168.0.31RHEL 10.0 (Coughlan)Practice node (graded) - spare /dev/sda2 Core4 GB50 GB + 15 GB
RHCSA-B01192.168.0.33RHEL 10.0 (Coughlan)Practice node 2 - spare /dev/sdb2 Core4 GB50 GB + 15 GB

Before you start

  1. Running a command somewhere else

    rhcsa-b01

    ssh host command runs one command and returns, rather than opening a session. That is the form worth having, because it composes: the output comes back on stdout and can be piped, redirected or captured locally.

     11:02:31 up  2:41,  0 users,  load average: 0.00, 0.01, 0.00
    sysadmin
    2

    Several commands in one go need quoting, because the semicolons would otherwise be interpreted by your local shell. And that raises the question the next step answers.

    bash Example session
    ssh rhcsa-b01 hostnamerhcsa-b01ssh rhcsa-b01 'uptime; id -un; nproc' 16:38:46 up 36 min,  4 users,  load average: 0.00, 0.00, 0.00sysadmin2ssh rhcsa-b01 'df -h /' | tail -2Filesystem             Size  Used Avail Use% Mounted on/dev/mapper/rhel-root   45G  4.8G   41G  11% /

    Expected resultCommands executing on the other machine.

    Success conditionYou can run a command remotely without opening a session.

  2. Which machine expands the variable

    The same command, differing only in quote character:

    ssh rhcsa-b01 "echo double quotes ran on: $(hostname)"
    double quotes ran on: rhcsa-a01
    ssh rhcsa-b01 'echo single quotes ran on: $(hostname)'
    single quotes ran on: rhcsa-b01

    Two different answers. Double quotes let your local shell substitute $(hostname) before ssh is even called, so the remote machine received a string that already said rhcsa-a01. Single quotes pass the text through untouched and the remote shell evaluates it.

    Neither is wrong - they are different tools. Local expansion is how you inject a local value into a remote command; remote expansion is how you ask about the remote machine. Getting them the wrong way round produces output that looks plausible and describes the wrong host, which is the worst kind of wrong.

    Default to single quotes for remote commands, and reach for double deliberately.

    bash Example session
    ssh rhcsa-b01 "echo double quotes ran on: $(hostname)"double quotes ran on: rhcsa-a01ssh rhcsa-b01 'echo single quotes ran on: $(hostname)'single quotes ran on: rhcsa-b01

    Expected resultThe same command naming two different hosts.

    Success conditionYou control which shell expands what.

  3. scp

    made on rhcsa-a01 at 11:02:41

    scp local remote:path pushes, and the file arrives:

    made on rhcsa-a01 at 11:02:41

    -r for a directory:

    /home/sysadmin/tree/sub/deep.txt

    and reversing the arguments pulls instead of pushes:

    Red Hat Enterprise Linux release 10.0 (Coughlan)

    The colon is what makes a path remote. scp file host/path with a slash instead of a colon quietly copies to a local file called host/path, which is a classic five-minute confusion.

    ~ on the remote side is expanded by the remote shell, so rhcsa-b01:~/ is that user's home there. An unqualified rhcsa-b01: means the same thing.

    bash Example session
    mkdir -p ~/xfer && cd ~/xfer && echo "made on $(hostname) at $(date +%T)" > note.txt && cat note.txtmade on rhcsa-a01 at 11:08:48cd ~/xfer && scp note.txt rhcsa-b01:~/ 2>&1 | tail -2; ssh rhcsa-b01 'cat ~/note.txt'made on rhcsa-a01 at 11:08:48cd ~/xfer && mkdir -p tree/sub && echo deep > tree/sub/deep.txt && scp -r tree rhcsa-b01:~/ 2>&1 | tail -1; ssh rhcsa-b01 'find ~/tree -type f'/home/sysadmin/tree/sub/deep.txtcd ~/xfer && scp rhcsa-b01:/etc/redhat-release pulled.txt 2>&1 | tail -1; cat pulled.txtRed Hat Enterprise Linux release 10.0 (Coughlan)

    Expected resultA file pushed, a directory pushed, and a file pulled back.

    Success conditionYou can move files in both directions.

  4. The rsync trailing slash

    This is the one to get right, because both forms succeed and only one does what you meant.

    Without a trailing slash - the directory itself is copied:

    /home/sysadmin/nodash
    /home/sysadmin/nodash/tree
    /home/sysadmin/nodash/tree/sub
    /home/sysadmin/nodash/tree/sub/deep.txt

    tree landed *inside* nodash, producing nodash/tree/....

    With a trailing slash - the directory's *contents* are copied:

    /home/sysadmin/withdash
    /home/sysadmin/withdash/sub
    /home/sysadmin/withdash/sub/deep.txt

    No tree level. The contents went straight into withdash.

    The rule: a trailing slash on the SOURCE means "the things in here", no slash means "this directory". The slash on the destination makes no difference.

    Run the wrong one against an existing target and you get backup/backup/etc/... instead of backup/etc/... - harmless but wrong, and on a restore it means the files are not where anything expects them.

    bash Example session
    cd ~/xfer && rsync -av tree rhcsa-b01:~/nodash/ 2>&1 | tail -4tree/sub/deep.txt sent 185 bytes  received 91 bytes  552.00 bytes/sectotal size is 5  speedup is 0.02ssh rhcsa-b01 'find ~/nodash | sort'/home/sysadmin/nodash/home/sysadmin/nodash/tree/home/sysadmin/nodash/tree/sub/home/sysadmin/nodash/tree/sub/deep.txtcd ~/xfer && rsync -av tree/ rhcsa-b01:~/withdash/ 2>&1 | tail -4sub/deep.txt sent 175 bytes  received 92 bytes  534.00 bytes/sectotal size is 5  speedup is 0.02ssh rhcsa-b01 'find ~/withdash | sort'/home/sysadmin/withdash/home/sysadmin/withdash/sub/home/sysadmin/withdash/sub/deep.txt

    Expected resultOne extra directory level in the no-slash form.

    Success conditionYou can predict the resulting tree before you run it.

  5. rsync only moves what changed

    Run the identical command again:

    sent 175 bytes  received 92 bytes  534.00 bytes/sec
    total size is 5  speedup is 0.02

    No file list. Nothing changed, so nothing was transferred - only the metadata needed to work that out. Change one file and it reappears:

    sub/deep.txt
    
    sent 234 bytes  received 92 bytes

    That is the difference from scp, which copies everything every time. On a large tree over a slow link it is the only sensible choice, and it makes rsync safe to re-run - which in turn makes it safe to put in a cron job.

    -a is the flag to remember: archive mode, which is recursive plus preserving permissions, times, symlinks and ownership. -v shows what moved. -av is the pair worth memorising, and --delete is the one to think twice about - it removes files from the destination that are gone from the source, which is either exactly what you want or a disaster.

    bash Example session
    cd ~/xfer && rsync -av tree/ rhcsa-b01:~/withdash/ 2>&1 | tail -3 sent 113 bytes  received 13 bytes  252.00 bytes/sectotal size is 5  speedup is 0.04cd ~/xfer && echo "new content" >> tree/sub/deep.txt && rsync -av tree/ rhcsa-b01:~/withdash/ 2>&1 | tail -4sub/deep.txt sent 180 bytes  received 42 bytes  444.00 bytes/sectotal size is 17  speedup is 0.08

    Expected resultA no-op run, then a run transferring only the changed file.

    Success conditionYou can re-run a sync safely and cheaply.

Troubleshooting

Official sources