BASH copy all files except one
I would like to copy all files out of a dir except for one named Default.png. It seems that there are a number of ways to do this. What seems the most effective to you?
Why do you need it to skip that file, as opposed to just deleting it after copying it? Does it exist in the target directory already?
@LasseV.Karlsen: Or you could want to save the time of copying it, if it’s a large file. I’m interested in this but excluding a directory rather than a file.
9 Answers 9
If copying to a folder nested in the current folder (called example in the case below) you need to omit that directory also:
cp -r !(Default.png|example) /example
It seems that OS X needs to use shopt -s extglob as described by @BarryKelly. With that, it works perfectly.
Years on Bash and didn’t know about !() . Beautiful! For those that —like me— feel it is time to study/review bash, here are the relevant links related to this question/answer: shopt/extglob and the pattern.
rsync has been my cp/scp replacement for a long time:
rsync -av from/ to/ --exclude=Default.png -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X) -v, --verbose increase verbosity
multiple —exclude= arguments are supported. And don’t forget the -r arg if you’re syncing directories
this»rsync -aP —exclude=backup /root/jenkins_api/* /root/jenkins_api/backup» does not work when there is no files in /root/jenkins_api/**.Is there a workaround to skip when no files are found ?
Simple, if src/ only contains files:
find src/ ! -name Default.png -exec cp -t dest/ <> +
If src/ has sub-directories, this omits them, but does copy files inside of them:
find src/ -type f ! -name Default.png -exec cp -t dest/ <> +
If src/ has sub-directories, this does not recurse into them:
find src/ -type f -maxdepth 1 ! -name Default.png -exec cp -t dest/ <> +
@Max \; executes the command once per file. + runs the command once and passes all of the file names to it at once (subject to the command line length limit). + is a bit more efficient in general.
cp `ls | grep -v Default.png` destdir
@arthur.sw For one thing, it doesn’t account for files with spaces in it. As with many things with the shell, there’s a bunch of edge cases that fail. 95%+ of the time, this stuff just works. Just need to be conscious of the caveats. Spaces in file names jump working with the shell from trivial to painful.
Another reason is that since there are no anchors in the regex and the dot isn’t escaped it would also match a file named myDefaultXpng.OLD for example.
cp srcdir/* destdir/ ; rm destdir/Default.png
unless the files are big. Otherwise use e.g.
find srcdir -type f/ |grep -v Default.png$ |xargs -ILIST cp LIST destdir/
The first command is not what the OP asked for. If Default.png exists in the two directories, it will replace the one in destdir by the one in srcdir , then delete the copied Default.png . Instead, the OP wants to keep the Default.png that already exists in destdir .
Jan, 2022 Update:
This is the easiest way(It’s not complicated).
First, make «temp» folder:
Second, copy all files from your original folder to «temp» folder:
«-R» flag can copy exactly all files including «Symbolic Links»
Third, remove «Default.png» from «temp» folder:
Finally, copy all files from «temp» folder to your destination folder:
cp -R temp/. destinationFolder/
In addition, this is the shortest way without «temp» folder:
cp -R originalFolder/!(Default.png) destinationFolder/
Below script worked for me
cp -r `ls -A | grep -v 'skip folder/file name'` destination
# chattr +i /files_to_exclude # cp source destination # chattr -i /files_to_exclude
use the shell’s expansion parameter with regex
Everything will be copied except for the not_to_copy_file
— if something is wrong with this. please Specify !
Welcome to SO. Unfortunately your answer is not correct. The bracket expresssion ( [. ] ) contains a set of characters to match, while a leading ^ will cause a match of the complement of the listed characters. In the following example, neither file will be listed: touch not_to_copy_file to_copy_file ; ls [^not_to_copy_file]* because all filenames starting with any of the following characters will be excluded: _cefilnopty .
Copy folder recursively, excluding some folders
I am trying to write a simple bash script that will copy the entire contents of a folder including hidden files and folders into another folder, but I want to exclude certain specific folders. How could I achieve this?
I imagine something like find . -name * piped to grep /v «exclude-pattern» to filter the ones you don’t want and then piped to cp to do the copy.
This should probably go to super user. The command you’re looking for is xargs. You could also do something like two tar’s connected by a pipe.
Maybe its late and it doesnt answer the question accurately but here’s a tip: If you want to exclude only immediate children of the directory you could take advantage of bash pattern matching, e.g. cp -R !(dir1|dir2) path/to/destination
Note that the !(dir1|dir2) pattern requires extglob to be turned on ( shopt -s extglob to turn it on).
9 Answers 9
rsync -av --exclude='path1/to/exclude' --exclude='path2/to/exclude' source destination
Note that using source and source/ are different. A trailing slash means to copy the contents of the folder source into destination . Without the trailing slash, it means copy the folder source into destination .
Alternatively, if you have lots of directories (or files) to exclude, you can use —exclude-from=FILE , where FILE is the name of a file containing files or directories to exclude.
—exclude may also contain wildcards, such as —exclude=*/.svn*
@AmokHuginnsson — What systems are you using? Rsync is included by default in all mainstream Linux distros I know of, including RHEL, CentOS, Debian, and Ubuntu, and I believe it’s in FreeBSD as well.
For RHEL derived distros: yum install rsync, or on Debian-based releases: apt-get install rsync . Unless you’re building your server from absolute base on your own hardware, this is a non-issue. rsync is installed by default on my Amazon EC2 boxes, as well and my boxes from ZeroLag and RackSpace.
Use tar along with a pipe.
cd /source_directory tar cf - --exclude=dir_to_exclude . | (cd /destination && tar xvf - )
You can even use this technique across ssh.
This approach unnecessarily first tars the target source (and exludes particular directories in the archive) and then untars it at the target. Not recommended!
@Waldheri you are wrong. This is the best solution. It does exactly what OP requested and it works on default install of most of the *nix like OSes. Taring and untaring is done on the fly with no file system artefact (in memory), cost of this tar+untar is negligible.
This is perfect when rsync is not available in your container and you don’t want to bother with installing it.
rsync went unavailable for us. So i updated the above tar a bit and this is what i came up with. tar -cf — —exclude=’./folder’ —exclude=’./file.tar’ ./source_directory | tar -xf — -C ./destination_directory
You can use find with the -prune option.
cd /source-dir find . -name .snapshot -prune -o \( \! -name *~ -print0 \)| cpio -pmd0 /dest-dir This command copies the contents of /source-dir to /dest-dir, but omits files and directories named .snapshot (and anything in them). It also omits files or directories whose name ends in ~, but not their con‐ tents. The construct -prune -o \( . -print0 \) is quite common. The idea here is that the expression before -prune matches things which are to be pruned. However, the -prune action itself returns true, so the following -o ensures that the right hand side is evaluated only for those directories which didn't get pruned (the contents of the pruned directories are not even visited, so their contents are irrelevant). The expression on the right hand side of the -o is in parentheses only for clarity. It emphasises that the -print0 action takes place only for things that didn't have -prune applied to them. Because the default `and' condition between tests binds more tightly than -o, this is the default anyway, but the parentheses help to show what is going on.
Copy all files in a directory to a local subdirectory in linux
I’d like to make a copy of all the existing files and directories located in this directory in new_subdir . How can I accomplish this via the linux terminal?
3 Answers 3
This is an old question, but none of the answers seem to work (they cause the destination folder to be copied recursively into itself), so I figured I’d offer up some working examples:
Copy via find -exec:
find . ! -regex ‘.*/new_subdir’ ! -regex ‘.’ -exec cp -r ‘<>‘ new_subdir \;
This code uses regex to find all files and directories (in the current directory) which are not new_subdir and copies them into new_subdir. The ! -regex ‘.’ bit is in there to keep the current directory itself from being included. Using find is the most powerful technique I know, but it’s long-winded and a bit confusing at times.
Copy with extglob:
cp -r !(new_subdir) new_subdir
If you have extglob enabled for your bash terminal (which is probably the case), then you can use ! to copy all things in the current directory which are not new_subdir into new_subdir.
Copy without extglob:
mv * new_subdir ; cp -r new_subdir/* .
If you don’t have extglob and find doesn’t appeal to you and you really want to do something hacky, you can move all of the files into the subdirectory, then recursively copy them back to the original directory. Unlike cp which copies the destination folder into itself, mv just throws an error when it tries to move the destination folder inside of itself. (But it successfully moves every other file and folder.)