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.

closed as unclear what you're asking by Kusalananda, roaima, Jesse_b, Thomas, schily Jul 20 '18 at 8:35

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 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
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
  • 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' "{}" \;
  • [[ grep ... ]] will not call grep. – Kusalananda Jul 19 '18 at 20:08
  • As mentioned, how to solve through the while loop? – Akand Jul 19 '18 at 20:13
  • @Akand What if a while loop reading the output of find is not a good solution? – Kusalananda Jul 19 '18 at 20:15
  • Good catch on the blatant syntax error, @Kusalananda. It must be Thursday; I never could get the hang of Thursdays. I've addressed that in my recent edit. – DopeGhoti Jul 19 '18 at 20:16
  • Good question! What is alternative solution if not using find in the while loop? I want to print the folder name after the searching within the while loop. – Akand Jul 19 '18 at 20:19
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:

  • 3
    This Q is a pile. – slm Jul 19 '18 at 20:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.