Grep – How to Grep Word Within a File and Copy the File

cpfindgreptext processing

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

Best Answer

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:

  1. 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 GNU grep.)

    • --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 by grep under GNU/Linux, MacOS and FreeBSD but not OpenBSD.)

    • LINUX/UNIX tells grep to look only for files whose contents include the regex LINUX/UNIX

    • . search in the current directory. You can omit it in recent versions of GNU grep, but then you'd need to pass a -- option terminator to cp to guard against file names that start with -.

  2. 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's xargs.)

    • cp -t /path/to/dest copies the directories to the target directory. (-t requires GNU cp.)

Related Question