7

Is there a way to copy files from folder a to folder b but have the process pause for a second between each file? I want to copy a whole lot of files to a watch folder but I know from experience that if I copy all of them at once (either through terminal cp or GUI) it will make my rtorrent crash... If i could copy the files with a second interval between them, that would give rtorrent the time to handle things. If I could do that from one command, that would save me from doing it one by one manually.

10

Single command solutions

Among many options, including loops that make use of cp and sleep, we could opt for find with two -exec flags to run those exact commands.

find ~/dir_1/ -type f -maxdepth 1  -exec cp {} ~/dir_2 \;  -exec sleep 1   \;

Here the options are:

  • ~/dir_1 , the origin directory, through which find works.
  • -type f get only files
  • -maxdepth 1 descend down 1 level maximum in the directory tree; prevents descending into subdirectories
  • -exec cp {} ~/dir_2 \; copy the currently processed file designated by {} parameter to ~/dir_2. The \; designates for cp to process single file at a time instead of tacking as many files as possible into {}. The backslash in \; is necessary to prevent shell from misinterpreting that as command terminator.
  • exec sleep 1 \; executes sleep 1 per each current file.

If you have a list of files in a file, you can do:

xargs -I % --arg-file=input.txt sh -c 'cp % ~/dir_2/ ;sleep 1'

Options here are:

  • -I % designates the symbol which refers to file within the command
  • --arg-file=input.txt read arguments to command from specific file
  • sh -c 'cp % ~/dir_2/ ;sleep 1' execute /bin/sh with provided commands by -c '...' flag; % will be substituted with filename.

Loop solutions

You could use a for loop for instance

for f in "/path/to/folder_1"/*; do
    cp "$f"  "/path/to/folder_2/"
    sleep 1
done

For clarity, this is formatted on multiple lines, but this works as a single line just as well. You could also turn this into a function:

delaycp(){
    for f in "$1"/*; do
        cp "$f"  "$2"/
        sleep 1
    done
}

And use it as

delaycp "/path/to/folder1" "/path/to/folder2"

Alternatively, if you want to copy only certain files, not everyting that * glob captures in the directory, you could always make use of pathname expansion, something like file{1..25}, or have an input text file, something like this input.txt:

/path/to/folder_1/file1
/path/to/folder_1/file2
/path/to/folder_1/file3

And from there make use of while IFS= read -r line structure to read the file:

while IFS= read -r line || [ -n "$line" ]; do
    cp "$line" "/path/to/folder_2/${line##*/}"
    sleep 1
done < input.txt
  • What is the "or" clause in your while loop intended to do? – Paused until further notice. May 25 '18 at 21:05
  • 2
    @DennisWilliamson Extra protection for case when file doesn't end in \n newline. Create this file as example: printf "Line one\nLine two" > input.txt. The while loop without that or clause will only get Line one. Why ? Because ` read` built in returns success only when line ends with \n. The Line two will still end up in the line variable, but read will return fail exit status. So that's why we need extra check in case the variable is nonempty. – Sergiy Kolodyazhnyy May 25 '18 at 21:11
  • In your explanation for the find solution, the first argument should be ~/dir_1 instead of ~/dir_2. The whole command is correct, just the explanation isn't. – Lienhart Woitok May 26 '18 at 7:40
2

With parallel

Here’s a parallel Install parallel solution, give the arguments to parallel with ::: exactly like with cp or use a file list:

parallel -j1 'cp {} /path/to/dir2/;sleep 1' ::: /path/to/dir1/* # or
parallel -j1 'cp {} /path/to/dir2/;sleep 1' :::: /path/to/file_list.txt

The -j option tells parallel to run only 1 job at once rather than running jobs in parallel, the single quotes simply contain the script where {} is replaced with the currently processed file and ::: or :::: introduce the argument list or file(s) respectively. See man parallel for much more.

With find -exec

find provides a convenient way of running commands on the findings, e.g. to copy every .log file in and under the current directory do:

find -name "*.log" -exec sh -c 'cp "$0" /path/to/dir2/;sleep 1' "{}" \;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.