1

Let say, I am in a directory A, under A there are many folders (B, C, D, etc.), and in each folder there is a file "*.out" and sub-folders. I want to run a script from A which will look for the text "index123" in *.out file and print out all corresponding folder names.

Here is my sript:

#!/bin/sh  
FILES=home/A  
grep --include=\*.out -rnw $FILES -e "index123" | while read file; do  
str1="FILES/$(basename $file)"  
echo $str1
done

This shows error.

N.B. This can be done by "find" in one line code, but why while loop shown shows error?

peterh
  • 9,731
Akand
  • 69

2 Answers2

2

Assuming a directory structure like the following:

A
|-- B
|   |-- file1.out
|   |-- file2.out
|   `-- file3.out
|-- C
|   |-- file1.out
|   |-- file2.out
|   `-- file3.out
|-- D
|   |-- file1.out
|   |-- file2.out
|   `-- file3.out
`-- E
    |-- file1.out
    |-- file2.out
    `-- file3.out

The issue with your code is that your grep will produce output that looks like

./B/file1.out:2:some data which includes the word index123
./B/file2.out:2:some data which includes the word index123
./B/file3.out:2:some data which includes the word index123
./C/file1.out:2:some data which includes the word index123
./C/file2.out:2:some data which includes the word index123
./C/file3.out:2:some data which includes the word index123
./D/file1.out:2:some data which includes the word index123
./D/file2.out:2:some data which includes the word index123
./D/file3.out:2:some data which includes the word index123
./E/file1.out:2:some data which includes the word index123
./E/file2.out:2:some data which includes the word index123
./E/file3.out:2:some data which includes the word index123

That is the output of

grep --include=\*.out -rnw . -e "index123"

with A as the current directory.

You will then try to run basename on these individual lines, which fails since basename takes at most two arguments (a pathame and a suffix to strip from that pathname). GNU basename will complain about an "extra operand" while BSD basename will complain about incorrect usage.


grep will show you the names of the files (only, i.e. not the complete line that matched) when you use it with the -l flag.

This means that your script may be replaced by the single command

grep -w -l "index123" */*.out

This will give output on the form

B/file1.out
B/file2.out
B/file3.out
C/file1.out
C/file2.out
C/file3.out
D/file1.out
D/file2.out
D/file3.out
E/file1.out
E/file2.out
E/file3.out

I added -w as you used in your grep command line. -n (for numbering lines, which you are also using) may not be used together with -l.

Judging from your code, this is what you want.

If you need just the folder names, then do

$ grep -w -l "index123" */*.out | sed 's#/[^/]*##' | sort -u
B
C
D
E

All of this assumes that A is the current working directory, but you said that this was the case in the question so it shouldn't be a problem.

Kusalananda
  • 333,661
  • Thanks! As I mentioned in my original post, this can be done by single line command. However, I am curious why the while loop is not working. The error message I've received is basename: extra operand ':'. Moreover, the command grep -w -l "index123" */*.out shows permission deny issue as because I am working in a server system with limited access. I need to look only within the working directory. – Akand Apr 11 '18 at 20:37
  • @Akand Well, if you look at what you actually get from your grep command, you will notice that there is a lot more than just the filename in the result. You are, for example requesting the line numbers... Also, I don't know how you're going to grep files in subdirectories without actually accessing the files in the subdirectories (they are there, not in the current directory). – Kusalananda Apr 11 '18 at 20:42
  • For clarification: I am in the directory A from where the code will scan *.out file of every folders of A by searching the text "index123" and then will print out the corresponding folder names. I like to do it by while loop as shown in the original post. – Akand Apr 11 '18 at 21:37
  • @Akand See updated answer. – Kusalananda Apr 12 '18 at 06:22
  • I appreciate your answer which does solve the problem. However, as mentioned in my post although there is solution of one line code, I like to do by while loop as I need to perform other operations. – Akand Apr 12 '18 at 18:55
1

As per from the post find a file through particular search in while loop one of solutions can be as follow by using the while loop:

#!/bin/bash
while IFS= read -r d;
grep -q "index123" "$d" && dirname "$d"|awk -F'/' '{print $2}'
done < <(find . -maxdepth 2 -type f -name "*.out")

Akand
  • 69