1

Actually it is related to my previous post sorting out directories by searching certain file extension which is still unanswered. There are many options available to solve the problem, but I am particularly interest via while loop. In a current working directory I want to search the existence of a particular file by scanning through certain content in a while loop and then print only the corresponding folder name. The current working directory has many folders and sub-folders. Here is my script:

while IFS= read -r d; do  
   if [[ "$d"=="*.out" && grep "index123"]]; then exit 1;  fi  
done <<  (find . -prune -type d) 

I do not know whether imposing if is a right approach to do inside the loop or something else. Please guide.

Akand
  • 69
  • You need spaces around your == and before ]] like: [[ "$d" == "*.out" && grep "index123" ]]. However the second part grep "index123" doesn't make sense. Maybe you want [[ "$d" == "*.out" && "$d" =~ index123 ]]. However overall I'm not sure what you are trying to accomplish. – jesse_b Jul 19 '18 at 19:24
  • What is grep "index123" suppose to grep against? You typically do something like echo $d | grep -q "index123". – slm Jul 19 '18 at 19:29
  • The *.out file should have the text "index123". That is why I used grep. I am trying to find out the *.out file by searching through "index123" and then print the corresponding folder name in the mentioned while loop. – Akand Jul 19 '18 at 19:55
  • @Akand - but you're not presenting it to grep to see it. Try it in a shell grep "index123" in a terminal will just hang, it's hanging b/c you haven't told it what to grep. – slm Jul 19 '18 at 20:00
  • grep from the file "*.out" which should have text "index123". – Akand Jul 19 '18 at 20:05
  • Is *.out supposed to match a directory or a file name? And is index123 supposed to be from the contents of a file or from a filename? – Kusalananda Jul 19 '18 at 20:21
  • file name; yes, contents of a file – Akand Jul 19 '18 at 20:25
  • The question is unclear: Where is the keyword index123 to be found? Is it part of a filename or found in the contents of some file. According to your own code, *.out should match directory names. Why do you use -prune like you do? – Kusalananda Jul 19 '18 at 20:26
  • Your last comment clarifies nothing I'm afraid. You say index123 should be found in a filename and in the contents of a file? – Kusalananda Jul 19 '18 at 20:27
  • "index123" is to be found in the file "*.out". If I understand well, prune here can be replaced as -maxdepth 1. – Akand Jul 19 '18 at 20:33
  • @Kusalananda, I have edited the post, and the question is solved. – Akand Jul 20 '18 at 17:49
  • @Kusalananda, it looks like my post still is on hold, I am not sure why? – Akand Jul 24 '18 at 13:48

3 Answers3

2

Why grep fails

Doing this in a terminal:

Fails - Waiting for input

$ grep "index123"

Works - Receives input from STDIN

$ echo -e "line1\nline2\nindex123blah\n" | grep "index123"
index123blah

The 2nd one works because we presented input for grep to parse via STDIN. grep will take input from either STDIN or a file. In your scenario:

if [[ "$d"=="*.out" && grep "index123"]]; then exit 1;  fi  

It's not parsing either, hence it's failing.

Your issue

From the looks of your code I believe you want something like this. To start here's some sample data:

$ mkdir -p 1/2/{3..5}
$ echo "index123" > 1/2/3/blah.out
$ echo "index123" > 1/2/3/blip.out
$ echo "index" > 1/2/blip.out
$ echo "index" > 1/2/blah.out

And a modified form of your script:

$ cat loopy.bash
#!/bin/bash

while IFS= read -r d; do  
  grep -l "index123" "$d" && exit 1
done < <(find . -type f -name "*.out") 

Running:

$ ./loopy.bash
./1/2/3/blah.out

Emits the first file it found that has index123 string, and exits.

While loop a good solution?

I wouldn't do it this way. Using a while loop here in this manner isn't necessary. It would be better to use a find .... | xargs ... architecture, or find ... -exec type of solution.

See @DopeGhoti's answer for a better approach to using find with either -exec or xargs.

Printing containing directory

If you simply want to print the containing directory tree where the matching file resides, you can use dirname to do this like so:

$ cat loopy2.bash
#!/bin/bash

 while IFS= read -r d; do
   grep -q "index123" "$d" && dirname "$d"  && exit 1
 done < <(find . -type f -name "*.out")

And running it:

 $ ./loopy2.bash
./1/2/3
slm
  • 369,824
  • I think I got my solution. I do not need exit 1 provided if I want to printout all folder paths. Also, I can use cut to get the folder names only. Thanks! – Akand Jul 20 '18 at 15:19
1

Here's your problem:

if [[ "$d"=="*.out" && grep "index123"]];

Let's pretend that d=foo.out. We effectively now have:

if [[ grep "index123"]];

So you're trying to tell grep to search standard input for the expression /index123/. But there would be no input coming to it, so it would (appear to) hang while it waits for input. However, it won't even get that far because if [[ grep "index123" ]] is invalid syntax on its face. You'd instead want if grep "index123". But again -- that will simply wait for standard input to come in.

It appears that you want to the returns from find for files named *.out which contain the text index123. So try this:

find . -prune -type d -name \*.out -print0 | xargs -0 grep -l 'index123'

That find statement is rather convoluted, though. Why find everything and then prune the directories when we could just search for the actual files we want?

find . -type f -name \*.out -print0 | xargs -0 grep -l 'index123'

Now you will see any files which contain the text for which you seek.

We don't even need to bring xargs to the table though, as find can invoke grep on its own:

find . -type f -name \*.out -exec grep -l 'index123' "{}" \;
DopeGhoti
  • 76,081
1

Finding all *.out files that contain the string index123 in the current directory:

grep -lF 'index123' ./*.out

If you want to do a recursive grep on all *.out files regardless of where they are beneath the current directory:

find . -type f -name '*.out' -exec grep -lF 'index123' {} +

Printing the directory where the string has been found:

find . -type f -name '*.out' -exec grep -qF 'index123' {} ';' -exec dirname {} ';'

Quitting after first found directory name (using GNU find):

find . -type f -name '*.out' -exec grep -qF 'index123' {} ';' -exec dirname {} ';' -quit

Depending on what you then are planning to use the directory name for, you would replace the -quit with another -exec to process the directory in whatever way you need to, alternatively combine the various -exec utilities into a shell script called from -exec. What you're not doing is to pipe the directory name to some other command, as that is generally unsafe unless you take extra precautions in delimiting the pathname(s) with nul characters.

Related:

Kusalananda
  • 333,661