Multiple Copies (cp) of a File with Bash

The syntax of the script will be:

./mcp main_file copy1 copy2 copy3

./mcp is the script name, main_file is the file that will be copied and copy1, copy2, copy3 and so on are the copies of main_file.

I started the script off by getting the main_file in $1 and with shift separating it from the copies:

#!/bin/bash

file=$1
shift

My second task was to make sure all the file paths were absolute and so I can check for duplicates. I do so with the following code:

arg_ls=$(
for arg in ${@}
do
        echo $(readlink -m $arg)
done
)

I use command substitution and run a for loop for all arguments (which are now all copies), and use the readlink -m which effectively turns relative paths to absolute paths. The paths are now in arg_ls with a space (‘ ‘) between them.

Next comes the process to remove duplicates with the following code:

uniq_ls=$(echo $arg_ls | tr ' ' '\n' | sort | uniq)

This is again command substitution to send the output to uniq_ls variable. In the  command substitution, the arg_ls is echoed and the output is send to tr to transform the space (‘ ‘) into newline (‘\n’) for the sort command, then the sort command gets duplicates to be consecutive and uniq removes any consecutive duplicates.

The combination getting absolute paths and removing duplicates ensures that pathnames don’t point to the same file.

The next code runs  uniq_ls through a for loop making copies of main_file for each path.

for path in $uniq_ls
do
        cp $file $path &
done

wait

Two things you might have noticed are the & and the wait. The & sends the process of copying the file to the background so the next process to copy the file begins. However, if a file is copying and the script ends, the process could become an “orphan” or “zombie”, to avoid that we use waitwait waits for the processes to end and then the script ends.

The final code is:

#!/bin/bash

file=$1
shift

arg_ls=$(
for arg in ${@}
do
        echo $(readlink -m $arg)
done
)

uniq_ls=$(echo $arg_ls | tr ' ' '\n' | sort | uniq)

for path in $uniq_ls
do
        cp $file $path &
done

wait