208

How do I diff files/folders across machines provided that the only connectivity available is ssh?

vvvvv
  • 175
  • 8
Alexey Timanovsky
  • 3,391
  • 3
  • 18
  • 8
  • 9
    There's a relatively similar question at [#16661: How can I diff two RedHat Linux servers?](http://serverfault.com/questions/16661/how-can-i-diff-two-redhat-linux-servers). The answer there, to save the clicks, is `rsync -ani --delete / root@remotehost:/` but [the full answer](http://serverfault.com/a/16665/86325) gives more detail. – Owen Blacker Feb 14 '12 at 18:30
  • Quite many answers here, but which approach is the faster? – pl1nk Aug 17 '12 at 18:26

16 Answers16

183

You can do it with Bash's process substitution:

 diff foo <(ssh myServer 'cat foo')

Or, if both are on remote servers:

diff <(ssh myServer1 'cat foo') <(ssh myServer2 'cat foo')
Kyle Brandt
  • 82,107
  • 71
  • 302
  • 444
  • 14
    yeah, something like this, but for directories :) – Alexey Timanovsky Aug 26 '09 at 17:29
  • How do you mean for directories, could always replace cat with ls – Kyle Brandt Aug 26 '09 at 17:29
  • 30
    I suspect that 'for directories' means doing a recursive diff – Ian Clelland Aug 26 '09 at 17:36
  • 10
    I found that this technique didn't work for me if my remote host required a password. The password prompt didn't seem to work well with the redirection and couldn't be successfully completed. If you use ssh keys this shouldn't be a problem. – Adam Franco Jan 24 '12 at 14:28
  • 2
    This is a great general solution to many problems. Instead of 'cat foo' you could use 'find /foo/bar -type f | while read file; do echo "**** $file"; cat "$file"; done' and you'd have a list of the file names and file contents to diff. – Paul Gear Nov 11 '14 at 22:22
  • @AdamFranco for those who use passwords, MFA, or other login methods that require a prompt, ControlMaster is helpful. It allows multiplexing over a single session. You can log into the host in one terminal window, then ssh in another terminal window can run without having to authenticate. Details here: https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing – vastlysuperiorman Mar 27 '19 at 17:00
121

Finally I've found great solution: vimdiff

vimdiff /path/to/file scp://remotehost//path/to/file

thanks to http://linux.spiney.org/remote_diff_with_vim_and_ssh see also http://www.vim.org/scripts/script.php?script_id=1075 .

Alexey Timanovsky
  • 3,391
  • 3
  • 18
  • 8
  • 11
    What about directories? :) – Perlnika Dec 16 '13 at 16:13
  • 1
    Here is an example: vimdiff .bash_profile scp://vaibhavc@10.217.212.97//home/vaibhavc/.bash_profile – vaichidrewar Feb 19 '14 at 22:56
  • 6
    2 slashes `//` after the `remotehost` is important. Single slash will not work – Lee Chee Kiam Apr 07 '14 at 02:05
  • 3
    If you have a different username on the remote host than your current host, you would use `vimdiff /path/to/file scp://username@remotehost//path/to/file` – JaredC May 19 '14 at 15:21
  • how can you do this with two remote hosts with id_rsa key files @JaredC? – Manatax Mar 12 '15 at 18:01
  • @Manatax, I'm not sure what you mean? If I have my id_rsa private key on my host, and my public key on the remote host, and locally I'm userA, but remotely I'm userB, then I need to include the username as part of my connection to the remote host. A shorthand way of doing that is like I showed above, prepending the username followed by @ before the host name. Let me know if I misunderstood your question. – JaredC Mar 15 '15 at 02:45
  • Might be worth mentioning it doesn't work if you need sudo to read the file on the remote host. – Antoine Cotten Feb 23 '17 at 15:07
71

If you just want to see what files are different, rather than a diff of the actual files, then you can use rsync --dry-run

Amandasaurus
  • 30,211
  • 62
  • 184
  • 246
17

Here's another quick and dirty command line recipe. Unlike the chosen answer, it works inside of makefiles:

ssh [login]@[host] "cat [remote file]" | diff - "[local file]"
Steve Dickinson
  • 171
  • 1
  • 2
  • @Steve Dickinson - what do you mean by "it works inside of makefiles" ? – Martin Vegter May 25 '14 at 13:43
  • 1
    This is also a nice solution if you're suffering from vim allergy like myself. :) I created a bash script that compares the same file on two different hosts, useful when migrating services between machines and comparing config files. See https://gist.github.com/jhqv/dbd59f5838ae8c83f736bfe951bd80ff – Janek Oct 14 '16 at 09:09
  • This solution also works if your remote host requires a password. – ishmael Jun 29 '20 at 00:33
10

Use scp to bring the files to a common machine and diff them there?

Or, if you just want to know if the files are different or not, hash them with md5sum on each machine.

You could also look into something like SSHFS, but I don't know how well an algorithm like diff performs over that.

Ian Clelland
  • 762
  • 1
  • 5
  • 7
8

One way, if it is possible on your system would be to simply mount the remote filesystem with sshfs.

Zoredache
  • 128,755
  • 40
  • 271
  • 413
  • 1
    diff is usually one-shot action, like to check config difference. Mounting filesystem each time is possible, but not convinient. I'd better tar and scp. – Alexey Timanovsky Aug 26 '09 at 17:28
7

You can use rsync in dry run mode, as suggested briefly in another answer. It lists any files that are different.

For that, use the rvnc options (r=recursive, v=verbose, n= dry-run, c=checksum). With rsync in pull mode (rsync [OPTION...] [USER@]HOST:SRC... [DEST]), an example is:

rsync -rvnc root@182.18.158.207:/var/www/html/dev/ .

Remember, this provides no info on whether the local or remote file is newer. Just if they differ.

zzapper
  • 341
  • 3
  • 7
2
  • on your local machine, make a recursive copy of the directory you want to diff. For instance:

    cp -R dir replica
    
  • use rsync to replicate the remote directory over the local copy:

     rsync remote:/path/to/dir replica
    
  • use diff to find difference between the local directory and the local replica of the remote one:

    diff dir replica
    
salva
  • 254
  • 1
  • 7
2

If you have sshfs and need to diff directories:

mkdir remote_path
sshfs user@host:/path/ remote_path
diff -r path remote_path
metrix
  • 121
  • 3
1

Here is how I did it.

I used SFTP to the remote server and entered my username/pwd when prompted. Then I used the dir that was created in the .gvfs dir in my home directory in the diff command.

diff -r --brief /home/user dir/.gvfs/SFTP\ on\ freenas.local/path to dir/dir1 /path to local dir/dir2
guntbert
  • 553
  • 7
  • 21
Peter
  • 11
  • 1
1

This is a script that can help to diff local folder and remote folder.:

 #!/bin/bash

 LOCALFOLDER=/var/www/tee
 REMOTEFOLDER=$(ssh root@111.111.111.1 'ls -lA /hfs/tee'| grep -E '^total' | cut -d " " -f 2 > remotessh.txt)
 COMMAND=$(ls -lA $LOCALFOLDER | grep -E '^total' | cut -d " " -f 2 > localssh.txt)
 REM=$(cat remotessh.txt)
 LOCAL=$(cat localssh.txt)

 echo $LOCAL
 echo $REM

 if [ $REM -eq $LOCAL ]
 then
      echo Directories are the same
 else
      echo Directories are differnt
 fi

 #diff localssh.txt remotessh.txt | grep -E '^total' | cut -d " " -f 2
mdpc
  • 11,698
  • 28
  • 51
  • 65
stainless
  • 11
  • 1
1

http://myfedora.co.za/2012/04/diff-two-remote-file-systems/

diff <(/usr/bin/ssh user1@192.168.122.1 'ls /opt/lib/') <(/usr/bin/ssh user2@192.168.122.1 'ls /tmp/') | grep -i ">" | sed 's/> //g'
guntbert
  • 553
  • 7
  • 21
Riaan
  • 411
  • 5
  • 13
1

Use sshfs mounted across ssh. It won't be all that fast, but you can use your full suite of tools that expect everything to be on a local filesystem. An alternative is NFS over a tunnel made with "ssh -w" (or other communications where ssh isn't the limitation).

Skaperen
  • 1,064
  • 2
  • 11
  • 21
1

You could also try generalizing the approach by creating a bash function, possibly in your ~/.bashrc:

myrdiff() { ssh root@"$1" cat "$2" | diff -s - "$2" ; }

then invoking it with a construct like:

myrdiff vm20-x86-64 /etc/gdm/Init/Default

By invoking diff with -s, this will also report if the files are identical.

guntbert
  • 553
  • 7
  • 21
Jim Swain
  • 11
  • 1
0

I use | cat at the end of the command for interactive password prompts and sdiff because it is easier to read than diff :

So between local and remote, type :

sdiff -s cat myfile<(ssh server1 'cat myfile') | cat

and between two servers, type :

sdiff -s <(ssh server1 'cat myfile') <(ssh server2 'cat myfile') | cat
SebMa
  • 275
  • 1
  • 9
0

using grep

remote ip: 172.16.98.130
remote file: /etc/ssh/keys-root/authorized_keys
local file: ~/.ssh/id_rsa.pub

Now compare the contents of remote file with the local file using grep

grep -w "$(cat ~/.ssh/id_rsa.pub)" <<< $(sshpass -p "mypassword" ssh root@172.16.98.130 'cat /etc/ssh/keys-root/authorized_keys')

check the success status of the grep command

echo $?
# 0

Using this idea to setup SSH passwordless login:

if ! grep "$(cat ~/.ssh/id_rsa.pub)" <<< $(sshpass -p "mypassword" ssh root@172.16.98.130 cat /etc/ssh/keys-root/authorized_keys) &>/dev/null; then
  cat ~/.ssh/id_rsa.pub | sshpass -p "mypassword" ssh root@172.16.98.130 "cat >> /etc/ssh/keys-root/authorized_keys"
fi

ssh root@172.16.98.130

Note: Make sure sshpass package is installed for passing password non-interactively to ssh command.

Sathish
  • 101
  • 3