Grep word within a file then copy the file
Solution 1
Try:
grep -rl --null --include '*.txt' LINUX/UNIX . | xargs -0r cp -t /path/to/dest
Because this command uses NUL-separation, it is safe for all file names including those with difficult names that include blanks, tabs, or even newlines.
The above requires GNU cp
. For MacOS/FreeBSD, try:
grep -rl --null --include '*.txt' LINUX/UNIX . | xargs -0 sh -c 'cp "$@" /path/to/dest' sh
How it works:
-
grep
options and arguments-r
tells grep to search recursively through the directory structure. (On FreeBSD,-r
will follow symlinks into directories. This is not true of either OS/X or recent versions of GNUgrep
.)--include '*.txt'
tells grep to only return files whose names match the glob*.txt
(including hidden ones like.foo.txt
or.txt
).-l
tells grep to only return the names of matching files, not the match itself.--null
tells grep to use NUL characters to separate the file names. (--null
is supported bygrep
under GNU/Linux, MacOS and FreeBSD but not OpenBSD.)LINUX/UNIX
tells grep to look only for files whose contents include the regexLINUX/UNIX
.
search in the current directory. You can omit it in recent versions of GNUgrep
, but then you'd need to pass a--
option terminator tocp
to guard against file names that start with-
.
-
xargs
options and arguments-0
tells xargs to expect NUL-separated input.-r
tells xargs not to run the command unless at least one file was found. (This option is not needed on either BSD or OSX and is not compatible with OSX'sxargs
.)cp -t /path/to/dest
copies the directories to the target directory. (-t
requires GNUcp
.)
Solution 2
More portably (POSIX features only):
find . -type f -name '*.txt' -exec grep -q LINUX/UNIX {} \; -exec cp {} /path/to/dest \;
Solution 3
The following sh/Bash one liner is another method, though will only work in the current directory, and doesn't recurse:
for f in ./*.txt; do if grep -l 'LINUX/UNIX' "$f"; then cp "$f" /path/to/dest/; fi; done
The -l
option to grep will print a list of the files which are being copied, though you could use -q
if you don't want to see anything on the screen.
Related videos on Youtube
![Admin](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Admin
Updated on September 18, 2022Comments
-
Admin almost 2 years
I have a collection of files ( *.zip, *.txt, *.tar.gz, *.doc, ...etc ). These files reside within a path. I want to find all the files ( *.txt), then copy, only, the text files that contain specific words ( e.g LINUX/UNIX).
I ran the following:
find . -name "*.txt" | grep 'LINUX/UNIX'
This command was able to find all the text files, then "grep" filtered the resultant text files by listing only the text files that contain 'LINUX/UNIX'.
How can I copy these final files (i.e. the text files that contain 'LINUX/UNIX') to a specific path of choice?
I tried to apply
xargs
find . -name "*.txt" | grep 'LINUX/UNIX' | xargs cp <to a path>
But it didn't work
-
Edward Falk almost 8 yearsFor Mac OS X, and probably BSD, you'll want to use --null instead of -Z. Also, I think
cp -t
is Linux-only. -
John1024 almost 8 years@EdwardFalk Good point. Thanks. I updated to use
--null
and added a version for BSD/OSX that does not usecp -t
. -
John1024 almost 8 years@StéphaneChazelas Thank you for the improvements.
-
Kusalananda about 6 yearsOpenBSD
grep
does not have--null
. -
John1024 about 6 years@Kusalananda Thanks. Answer updated to note that OpenBSD does not support
--null
. -
Michal Przybylowicz almost 6 yearsIt should be noted here that some grep colorize the output (in other words is puts additional characters into the name of the file). To silence color character use
--color=never
. -
John1024 almost 6 years@MichalPrzybylowicz That is a good point. Sometimes, a
grep
alias is used to turn color on. Such aliases should use--color=auto
rather than--color=always
. With--color=auto
, grep will only colorize if the output is going to a terminal (as opposed to, say, a pipeline as is our case here).