219

I have a large folder of pictures (thousands), and I have a long list of files, by exact file name, that I need to copy to another folder. I want to know if there is a way I can select several specific files from this folder, by name, and copy them to another folder, using the terminal, without copying them individually?

1
  • 1
    Fyi, for copying specific folders use: cp -rp /copying/from/{folder1/,folder2/,folder3/} path/to/folder, where p is for copying the folder permission. Feb 21, 2019 at 8:31

7 Answers 7

306

Simply copy multiple files at once from command line

There are several ways you could achieve this. The easiest I have seen is to use the following.

cp /home/usr/dir/{file1,file2,file3,file4} /home/usr/destination/

The syntax uses the cp command followed by the path to the directory the desired files are located in with all the files you wish to copy wrapped in brackets and separated by commas.

Make sure to note that there are no spaces between the files. The last part of the command, /home/usr/destination/, is the directory you wish to copy the files into.

or if the all the files have the same prefix but different endings you could do something like this:

cp /home/usr/dir/file{1..4} ./

Where file1,file2,file3 and file4 would be copied.

From how you worded the question I believe this is what you're looking for but it also sounds like you might be looking for a command to read from a list of files and copy all of them to a certain directory. If that is the case let me know and i'll edit my answer.

Dealing with duplicates with python

So I wrote a little python script that I believe should get the job done. However, I am not sure how well versed you are in python (if versed at all) so I will try explaining how to use this script the best I can and please ask as many questions about it as you need.

import os,sys,shutil
### copies a list of files from source. handles duplicates.
def rename(file_name, dst, num=1):
    #splits file name to add number distinction
    (file_prefix, exstension) = os.path.splitext(file_name)
    renamed = "%s(%d)%s" % (file_prefix,num,exstension)

    #checks if renamed file exists. Renames file if it does exist.
    if os.path.exists(dst + renamed):
        return rename(file_name, dst, num + 1)
    else:
        return renamed

def copy_files(src,dst,file_list):
    for files in file_list:
        src_file_path = src + files
        dst_file_path = dst + files
        if os.path.exists(dst_file_path):
            new_file_name =  rename(files, dst)
            dst_file_path = dst + new_file_name
            
        print "Copying: " + dst_file_path
        try:
            shutil.copyfile(src_file_path,dst_file_path)
        except IOError:
            print src_file_path + " does not exist"
            raw_input("Please, press enter to continue.")

def read_file(file_name):
    f = open(file_name)
    #reads each line of file (f), strips out extra whitespace and 
    #returns list with each line of the file being an element of the list
    content = [x.strip() for x in f.readlines()]
    f.close()
    return content

src = sys.argv[1]
dst = sys.argv[2]
file_with_list = sys.argv[3]

copy_files(src,dst,read_file(file_with_list))

This script should be relatively simple to use. First off, copy the above code into the program gedit (should be pre-installed in Ubuntu) or any other text editor.

After that is complete, save the file as move.py in your home directory (it can be any directory but for ease of instruction lets just use the home directory) or add the directory the file is contained in to your PATH. Then cd to your home directory (or whatever directory you saved move.py in) from the terminal and type the following command:

python move.py /path/to/src/ /path/to/dst/ file.txt

This should copy all of the files that are listed from the source directory to the destination directory with duplicates taking the format pic(1).jpg, pic(2).jpg and so on. file.txt should be a file that lists all the pictures you would like to copy with each entry on its own separate line.

In no way should this script effect the source directory, however just make sure to enter the correct paths to the source and destination directory and the worst that could happen is you copy the files to the wrong directory.

Notes

  • This script assumes that all of the original pictures are in the same directory. If you want it to check sub directories as well the script will need to be modified.
  • If you accidentally mistype a file name, the script will spit out the error
    "file does not exist" and prompt you to "press enter" to continue and the script will continue copying the rest of the list.
  • Don't forget the trailing / on both the path to the source
    directory and path to the destination directory. Otherwise the script will spit an error back at you.
7
  • Thank you, that solved most of my problem, however, some files appear in the list twice or more, How can I make it auto-rename these duplicates? (Yes, I need the duplicates for what I'm doing.) Aug 1, 2013 at 6:09
  • Are the duplicates from different sub folders or are the labelled something like file.jpg and file(1).jpg? otherwise I am not sure how you have files that are identical. To somewhat answer your question, I don't believe you are going to be able to find a simple on liner that will auto rename duplicates for you. I think a bash or some other type of script would have to be written to get that type of functionality. However, I have seen some pretty ridiculous one line commands so maybe some linux wizard may show up and provide the answer.
    – Bryan
    Aug 1, 2013 at 6:17
  • I only have one of each file originally, however the names of the files are in the list twice or more. More or less what I need for it to do, when trying to place a file in the new folder, and a file by that name already exsists, I need for it to add a (2) or so to the end of the file name and place it. Aug 1, 2013 at 6:22
  • 1
    I just want to suggest to emphasize the part about having no spaces in the list of files to copy. If there are any spaces, even between files {foo, bar,..} the command will not work.
    – franksands
    Nov 19, 2018 at 13:00
  • 1
    thanks for the tip about the spaces, i wouldve assumed it wasnt working otherwise. Jun 13, 2020 at 18:11
104

Perhaps I'm missing a detail of your question, but the answers given seem excessive. If you want a command line solution and not a script, why not:

cd /path/to/src/
cp -t /path/to/dst/ file1 file2 file3 ...

The nice thing about doing it this way is that you can tab complete the file names

7
  • 9
    This returns an "illegal option --t" in macOS Sierra
    – geoyws
    Oct 20, 2016 at 7:01
  • 1
    Great answer, far simpler. @geoyws Maybe it's a POSIX or GNU thing that Apple's version doesn't have. It works fine for me on Debian Linux or Windows with MSYS2. Oct 22, 2016 at 14:38
  • nice suggestion! worked in arch linux.
    – Edu Ruiz
    Mar 31, 2017 at 13:31
  • 3
    Mac OS uses BSD version of cp, and particularly neither OpenBSD nor FreeBSD have that option, so yes - this is GNU cp specific. It's also not specified by POSIX standard. Since this is Ubuntu specific site, this is acceptable, but for porting scripts between OS's it's better to stick to POSIX standard May 26, 2018 at 7:36
  • 1
    Direct and easy answer. Adding one more thing. if you need to copy a directory which have contents add -r option also. Sep 21, 2020 at 14:50
8

Here's a pure bash solution. It will read file names from an input file (one per line) and copy each of them, renaming duplicates.

#!/usr/bin/env bash

## The destination folder where your files will
## be copied to.
dest="bar";

## For each file path in your input file
while read path; do 
    ## $target is the name of the file, removing the path. 
    ## For example, given /foo/bar.txt, the $target will be bar.txt.
    target=$(basename "$path"); 
    ## Counter for duplicate files
    c=""; 
    ## Since $c is empty, this will check if the
    ## file exists in target.
    while [[ -e "$dest"/"$target"$c ]]; do
        echo "$target exists"; 
        ## If the target exists, add 1 to the value of $c
        ## and check if a file called $target$c (for example, bar.txt1)
        ## exists. This loop will continue until $c has a value
        ## such that there is no file called $target$c in the directory.
        let c++; 
        target="$target"$c; 
    done; 
    ## We now have everything we need, so lets copy.
    cp "$path" "$dest"/"$target"; 
done

Save this script in a folder in your $PATH and call it with the list of paths as input:

auto_copy.sh < file_paths.txt

You can also run the entire thing as a command from the terminal:

while read path; do 
   target=$(basename "$path"); 
   c=""; 
   while [[ -e bar/"$target"$c ]]; do 
    echo "$target exists"; 
    let c++; 
    target="$target"$c; 
   done; 
   cp "$file" bar/"$target"; 
done < file_names;
0
3

As per description of the question, my understanding is that:

  • there is a list of files, presumably a text file input.txt
  • the list contains filenames only
  • there is a particular directory where these filenames are located.

Thus, one can make use the following command:

xargs -I % --arg-file=input.txt cp  /path/to/origin_dir/%  /path/to/destination

Explanation:

  • -I % specifies symbol for currently processed file to be used within command
  • --arg-file=input.txt specifies to take arguments to command from input.txt
  • cp /path/to/origin_dir/% /path/to/destination/ will perform cp command with /path/to/origin_dir/% being replaced with /path/to/origin_dir/ and name of currently processed file.

Practical example:

$ cat input.txt
file2.txt
file1.txt
file3.txt
$ ls ./docs
file1.txt  file2.txt  file3.txt
$ xargs -I % --arg-file=input.txt cp ./docs/% ./docs_destination/
$ ls ./docs_destination/
file1.txt  file2.txt  file3.txt
1
  • 1
    I'm getting cp: cannot stat '/path/to/origin_dir/file2.txt'$'\r': No such file or directory cp: cannot stat '/path/to/origin_dir/file1.txt'$'\r': No such file or directory May I know why this happens (given all 3 files ARE in the origin_dir)? Basically, only the last filename in the txt will get successfully copied to the new destination while the previous ones all failed
    – uniquegino
    Apr 7, 2023 at 18:53
3

From cp --help:

cp [OPTION]... SOURCE... DIRECTORY

The SOURCE... with three commas means multiple params i.e. you can just make

cd your_source_dir
cp file1 file2 dest_dir
0

Having hit this a few times, my solution is a small script to cp everything into a folder so it looks exactly like I want the archive (folders and all), then cd <folder_to_be_zipped> and zip -r ../<zip_file_name>.zip . and I will end up with a .zip where I want it, having exactly the structure I want.

Another way is to use multiple zip commands such as:

cd <parent_of_directory> && zip -r /path/to/archive.zip directory/* && \
cd <folder_with_files> && zip /path/to/archive.zip addfile1 addfile2 addfile2;

Samples courtesy of: https://wpguru.co.uk/2018/12/how-to-add-files-to-an-existing-zip-archive-on-macos-and-linux/

0

Here the command for not specific file but you can copy or move all files from one directory to another directory.

Run below command

find path/to/origin/dir -type f -print0 | xargs -0 cp -t path/to/destination/dir

for move all the files, replace cp to mv in the above command

You must log in to answer this question.

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