loading...

Copy file to multi destinations at once - Linux Tips

0xbf profile image Bo ・2 min read

TL;DR

Use xargs, for example: echo "dest1 dest2" | xargs -n 1 cp src

Details

I am sure you are familiar with the cp command: cp src dest

If we want to copy one file to multi destinations, we can use xargs with the cp command.

xargs will take previous command's stdout as its following command's CLI parameter. Let's learn it with some examples:

1, Copy one file to multi files. Say we have "hello.txt" in our current folder, we want to make copy it as "hello1.txt" and "hello2.txt", we can do:

$ echo "hello1.txt hello2.txt" | xargs -n 1 cp hello.txt
$ ls 
hello.txt  hello1.txt  hello2.txt

The -n 1 after xargs means we want to take the output as cp's CLI parameter one by one, so the above command is like:

$ cp hello.txt hello1.txt
$ cp hello.txt hello2.txt

2, Copy one file into several folders. Say if under the current folder, we have hello.txt and 2 folders: folder1 and folder2, and we want to copy hello.txt to all subfolders (folder1 and folder2) under the current folder:

The first thing we need to do, is to use a command to list all subfolders. We have 2 ways to do this:

1) use ls -d

$ ls -d */
folder1/  folder2/

2) use find . -type d

$ find . -maxdepth 1 ! -path . -type d
./folder1
./folder2

Notice we use -maxdepth 1 here to skip sub-sub folders and use ! -path . to skip current folder path "./"

Now we can combile this with xargs command to copy the file to those folders:

$ ls -d */ | xargs -n 1 cp hello.txt
$ ls folder*
folder1:
hello.txt

folder2:
hello.txt

3, So far in our example, what xargs does is appending the previous command's stdout to its following command's command parameter, but we can also inserting it to its following command's parameter with the -I option.

For example, say if we want to delete hello.txt under both folder1 and folder2, we can do this:

$ ls -d */ | xargs -n 1 -I{} rm {}hello.txt
$ ls folder*
folder1:

folder2:

We can see the hello.txt file is gone under those 2 folders. In this example, the ls -d */ will print folder1/ and folder2, then with the -I option in xargs, we kind of assign those stdout to a variable or a placeholder {}, then concatenate the variable {} with hello.txt, so essentially the above command is equal to:

rm folder1/hello.txt
rm folder2/hello.txt

Another way to do the multi deletion is, since rm can take multiple parameter, as long as we find all hello.txt under those folders, we can the simply use xargs to pass it to rm:

$ ls folder*/hello.txt | xargs rm 

This is equal to:

$ rm folder1/hello.txt folder2/hello.txt

Or if we want to delete "hello.txt" under all folders:

$ find . -name hello.txt | xargs rm

Reference:

Discussion

pic
Editor guide
Collapse
trasherdk profile image
TrasherDK

Here's a variant of the same theme: distribute.sh

#!/bin/bash

[ $# -eq 0 ] \
    && {
        echo "${CYAN} Usage ${WHITE} $(basename $0) /path/to/file ${YELLOW}[/destination/directory]${RESTORE}"
        exit
    }

[ -f "$1" -a ! -d "$1" ] \
&& {
    SCP="scp"
    FILE="$(realpath $1)"
    shift
} \
|| {
    [ -d "$1" ] \
    && {
        RECUR="recursive"
        SCP="scp -r"
        FILE="$(realpath $1)"
        shift
    } \
    || {
        echo "${RED} Error: ${WHITE} Unknown file or directory.${RESTORE}"
        exit 1
    }
}

[ $# -gt 0 ] \
    && {
        DESTDIR="$(realpath $1)/"
    } \
    || {
        DESTDIR="$(dirname $(realpath ${FILE}))/"
    }

echo -e "${WHITE}Copying ${RECUR}\nFrom: ${YELLOW}${FILE}\n${WHITE}To: ${YELLOW}${DESTDIR}${RESTORE}"


for DEST in $(cat ~/.ssh/authorized_keys | cut -f 3 -d ' ' | tr -d ' ' )
do
    DESTUSER=$( echo ${DEST} | cut -f 1 -d '@' | tr -d '[:space:]' )
    DESTHOST=$( echo ${DEST} | cut -f 2 -d '@' | tr -d '[:space:]' )

    echo -n "${CYAN}===>${WHITE} ${DESTUSER}${YELLOW} @${WHITE} ${DESTHOST}"

    if ping -c 1 -w 2 -nq ${DESTHOST} > /dev/null 2>&1
    then
        echo " ${GREEN}OK${RESTORE}"
    else
        echo " ${RED}NOT OK${RESTORE}"
        continue
    fi

    echo "${CYAN} Copy ${WHITE}${FILE} to ${DESTDIR}"
    $SCP ${FILE} ${DESTHOST}:${DESTDIR}

done