7

i'm write a script for automation copying file with rsync from server-a to server-b this is my script :

#!/bin/bash
NOW=$(date +"%Y-%m")
rsync -au --ignore-existing /var/www/uploads/$NOW/* -e [email protected]:/var/www/uploads/$NOW/.

when we are going to an next month like from October to November , i get this error on my script :

`

rsync: mkdir "/var/www/uploads/2014-11/." failed: No such file or directory (2)
rsync error: error in file IO (code 11) at main.c(605) [Receiver=3.0.9]
rsync: connection unexpectedly closed (9 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(605) [sender=3.0.9]

` how should i fix this error ? please help me

4 Answers 4

7

Firstly, make sure that the parent directory on the target exists. I.e. [email protected]:/var/www/uploads should exist. I think with your formulation, the trailing . refers to a directory which rsync tries to create, and it can't do so unless the parent directory already exists. ie the parent is [email protected]:/var/www/uploads/$NOW.

Secondly, realise that the behaviour of rsync is subtly different to cp in various ways with the trailing '/' on the file name. I find the safest and most intuitive way to do things is to use a trailing / on the end of all directories. Like so:

rsync -au --ignore-existing /var/www/uploads/$NOW/ -e [email protected]:/var/www/uploads/$NOW/

Unlike cp, rsync will reliably copy the content of the directory in the source argument to the content of the target directory, creating the target directory if necessary (though it's parent must exist), and not putting the source directory (ie the parent) inside the target directory if the target already exists.

This is slightly different to what you were doing though in that the way you did it would exclude files with a name starting with . in the source directory, and would fail if the list of files being copied was too long (bash expansion capped at a total command line length of around 32K characters if memory serves me right).

2

The trailing dot needs to be removed, change the script to:

rsync -au --ignore-existing /var/www/uploads/$NOW/* -e [email protected]:/var/www/uploads/$NOW
1

Try this to have rsync create all needed directories on the destionation:

NOW=$(date +"%Y-%m")
rsync -au --ignore-existing --relative /var/www/uploads/$NOW [email protected]:/var/www/uploads/
  • added --relative to have rsync create the directory tree on the destination.
  • No slash after source, so that the last directory ("$NOW") also gets created on the destination, not only it's content.
  • Removed "$NOW" from destination since it will be created if needed.
  • removed your lonely -e option which looks like a mistake (it would need an argument: "This option allows you to choose an alternative remote shell program to use for communication").

Or see this answer for more details and alternatives.

1
0

I encountered this error running rsync locally (from a python script), not by dir, but looping through dirs and subdirs via os.walk to get to the folders that have files, and then rsync'ing each file with multiprocessing.pool with starmap. I searched for an answer for this scenario for a long time, and finally I found this link that presented the solution;

Through os.walk, I grabbed the root dir, and if there were files associated with that root dir, I then call rsync with the -aR flags;

        import subprocess
        import os
        import sys
        import multiprocessing
        from util_subprocess import exec_subprocess

        ORIG_SRC_ROOT = '/home/username/workspaces/data/prod'
        # IMPORTANT (slash, period) - we need to tell rsync 
        # that we want it to create subdirs relatively after the period
        # in the DEST_ROOT dir
        REL_SRC_ROOT = ORIG_SRC_ROOT + '/.'
        # rsync will create the directory structure for you below the DEST_ROOT
        DEST_ROOT = '/home/username/workspaces/data/prod_bak'
        
        CPU_COUNT = multiprocessing.cpu_count()
        if(not CPU_COUNT):
            CPU_COUNT = 1
    
        def sync(src, dest):
            '''
            Make the call the subprocess.run to execute rsync.
            
            NOTE:
            The flags are key here:
                --  '-a' (-a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X))
                is pretty standard and covers most everything we need. 
                --  '-R'(-R, --relative              use relative path names), 
                makes sure rsync creates the subdirs on the destination folder.
            '''

            flags: str = r"-aR"
            program_name: str = r"rsync"
            exec_list = [program_name, flags , src, dest]
            return exec_subprocess(exec_list)
    
        def get_sync_params():
            '''Get a list of tuples, each tuple containing the src_file path, and dest_dir path.'''

            sync_params = []
            for root, dirs, files in os.walk(ORIG_SRC_ROOT, topdown=False):
                if(files):
                    for file in files:
                        src_file_temp = os.path.join(root, file)
                        # replace the orig root dir with the relative one from above 
                        src_file = src_file_temp.replace(ORIG_SRC_ROOT, REL_SRC_ROOT)
                        dest_dir = DEST_ROOT
                        # place them into a tuple
                        sync_tuple = (src_file, dest_dir)
                        # add the tuple to the list
                        sync_params.append(sync_tuple)
            return sync_params
    
        if __name__ == "__main__":
            '''Create a pool for multiprocessing and get these done in parallel.'''

            # call the get_sync_params above
            sync_params = get_sync_params()
            # Create a pool of specific number of CPUs
            print("Create {} processes in our pool.".format(CPU_COUNT))
            pool = multiprocessing.Pool(processes=CPU_COUNT)
            '''
            Start each task within the pool -  
            call the 'sync' function above 
            (which calls a separate wrapper function 
            to the subprocess.run call (exec_subprocess)). 
            The return value is a list of tuples 
            and each tuple contains the output (stdout OR stderr) 
            and the return_code results for each pair of src & dest input.
            '''
            results = pool.starmap(sync, sync_params)

So essentially we create a list of tuples where each tuple is the src_file path and the dest_dir path, handled by multiprocessing.pool.starmap(function_name, tuple_list), which in turn uses a process in the pool for each tuple which is sent to rsycn over subprocess.run. A tuple in the list might look like;

[('/home/username/workspaces/data/prod/./math/alg_1/alg_1_test.txt',
'/home/username/workspaces/data/prod_bak/'), ('...', '...'), ]

The first string is the src_file path, the second is the dest_dir path. Notice the '.' in the src_file path (telling rsync we need to create subdirs after this in the destination), and the trailing '/' in the dest_dir path, telling rsync to put those new subdirs under this destination root dir.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .