4

I have a large set of files in a directory. The files contains arbitrary text.

I want to search for the file name inside that particular file text. To clarify, I have file1.py.txt (yeas, two dots .py.txt) and file2.py.txt both contains texts. I want to search for the existence of the string @code prefix.file1.py inside file1.py.txt and for the string @code prefix.file2.py inside file2.py.txt

How can I customize grep such that it goes through every file in the directory, search for the string in each file using that particular file name?

EDIT:

The output I am looking for is written in a separate file, result.txt which contains: filename (if a match is found), the line text (where the match is found)

1
  • What output are you wanting? A list of filenames that satisfy the condition, or the list of lines from those files? Mar 7, 2019 at 14:22

2 Answers 2

8

With GNU awk:

gawk '
  BEGINFILE{search = "@code prefix." substr(FILENAME, 3, length(FILENAME) - 6)}
  index($0, search)' ./*.py.txt

Would report the matching lines.

To print the file name and matching line, change index($0, search) to

  index($0, search) {print FILENAME": "$0}

Or to print the file name only:

  index($0, search) {print FILENAME; nextfile}

Replace FILENAME with substr(FILENAME, 3) to skip outputting the ./ prefix.

The list of files is lexically sorted. The ones whose name starts with . are ignored (some shells have a dotglob option to add them back; with zsh, you can also use the (D) glob qualifier).

2
  • your solution should be faster. This is printing the string @code ..., while mine prints the filename.
    – Jay jargot
    Mar 7, 2019 at 13:37
  • @Jay, it would be faster as longer as if the files are relatively small as only one command is run. For large files, running several greps may end up being faster as grep implementations are generally faster at matching than gawk. I've added alternatives to only print the file names. Mar 7, 2019 at 13:47
3

It is needed to grep each file found.

-l instructs grep to print the filename only when the regex is found.

If the filenames does not contain any / char, give a try to this:

find a_directory -type f -name \*.py.txt -exec sh -c '
  for fname; do
    basename="${fname##*/}"
    grep -lF "@code prefix.${basename%.*}" "${fname}"
  done' sh {} +

see man bash for the items below:

  • "${fname##*/}" is file1.py.txt, if fname == a_directory/file1.py.txt
  • "${basename%.*}" is file1.py, if basename == file1.py.txt
5
  • There is no need to use find at all if the files are in a given directory, just loop through the results of print *.py.txt Mar 7, 2019 at 13:23
  • @StephaneChazelas: thx! I will switch soon to -exec ... instead of | xargs. I did not thought about the fgrep
    – Jay jargot
    Mar 7, 2019 at 13:35
  • @RamanSailopal: in general ... yes. In this particular situation, a substring of the filename is searched inside the text file, so I do not think there is a solution with grep ... *.py.txt
    – Jay jargot
    Mar 7, 2019 at 14:42
  • 2
    @Jay, you could always do for fname in *.py.txt Mar 7, 2019 at 15:02
  • Tested ok with filenames with exotic chars (",` , \n) ; I am surprised because the for` loop did not break.
    – Jay jargot
    Mar 7, 2019 at 15:41

You must log in to answer this question.

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