0

I am very new to shell scripting. Can anybody please explain me below lines in a very simple language or give me some link where I could find exactly the meanings I am searching for as written in code below:

How file is getting values? Is it getting from grep command written after done?

dir=$1 str=$2
while IFS= read -rd '' file;   #What '' is doing?
do
    base=${file##*/}  #Please explain
dir=${file%/*}    #Please explain
done < <(exec grep -ZFlR "$str" "$dir")

Thanks a ton in advance :)

user3542109
  • 121
  • 1
  • 3
  • 11
  • This is several questions in one. See, for example, http://stackoverflow.com/questions/2059794/ http://stackoverflow.com/questions/16444004/ http://stackoverflow.com/questions/8677546/ – user123444555621 Aug 08 '14 at 06:54

3 Answers3

2

The first line is used to read raw input passed by grep -ZFlR "$str" "$dir".

while IFS= read -rd '' file;

As you have specified -Z in grep it'll output a zero byte instead of a newline separated file names which usually grep does. So in read command is also specified with a -d option referring the delimiter. As for IFS= that is done to empty out IFS(Internal Field Separator) so as to preserve leading and trailing whitespace. More read here

The next line :

base=${file##*/}

deletes the longest match of any charater ended by a slash from the front of $file.So something like :

/abc/def/jhg

-------->

strips off /abc/def/

Similarly the third line :

dir=${file%/*}

deletes the shortest match from the end of $file.

/abc/def/jhg

        <----

strips off /jhg. 

More read here.

As you didn't ask about the last line, I am assuming you're familiar that it is being redirected to the while loop.

knittl
  • 246,190
  • 53
  • 318
  • 364
Ashish Gaur
  • 2,030
  • 2
  • 18
  • 32
  • Why is the last line using `exec` and process substitution? What's the difference between `while read ...; do ...; done < <(exec grep ...)` and `grep ... | while read ...; do ...; done`? I would have assumed a pipe is the way to go. Please explain :) – knittl Aug 08 '14 at 06:56
  • @knittl I don't know why `exec` is present there, it works fine without it and should be like that only. Regarding your second question I found a good explanation here => http://stackoverflow.com/questions/3177507/how-to-do-a-while-loop-with-a-string-redirected-into-it. Although it doesn't apply to this case but generally speaking. Hope it helps ;). – Ashish Gaur Aug 08 '14 at 07:26
  • `<(exec ...)` and `$(exec ...)` is konsolebox's style ;) – konsolebox Aug 08 '14 at 07:33
  • @konsolebox That's ok :), but why use `exec`, just doing `<(grep something)` is sufficient or for that matter `$(grep something)`. – Ashish Gaur Aug 08 '14 at 07:37
  • 3
    Because without `exec`, the shell by default summons another fork and use that fork to execute the binary. We don't need to do anything after that so we just use the subshell itself to execute the binary instead. This saves extra forking and also makes the process id presented by `$!` the actual process of whatever `exec` calls. – konsolebox Aug 08 '14 at 07:43
  • @knittl I hope I get credit if suddenly those stuffs get to wikis :) But we know such authors never do that :P – konsolebox Aug 08 '14 at 07:49
2
while IFS= read -rd '' file;   #What '' is doing?
do

Thie while loop will read each line returned by the command (exec grep -ZFlR "$str" "$dir"). You see it is being used to 'feed' data to the loop at the end: done < <(exec grep -ZFlR "$str" "$dir") Beginning with the while loop you see IFS=. That unsets the Internal Field Separator (IFS) in bash which determines what separates a given string of words into separate fields. (the defaults IFS=$'space tab newline' which you see written like IFS=$' \t\n')

The while loop continues with read -rd '' file; As discussed the input is coming from the exec grep.. expression at the end, and read -rd '' file is reading that input up to the first '' which is specified by -d to be the delimeter to use with this read. read then stores the matching input in the variable file. So the '' is just serving as the delimeter for read as specified by the -d option to read. (that explains why IFS was unset at the beginning, they wanted to use the specific '' delimiter in this case.

    base=${file##*/}  #Please explain

All this says is use parameter expansion to delete eveything in string beginning from the left up to (and including) the last / character. (that is what ## means). It is stipping the path information from the filename leaving only the filename in base.

dir=${file%/*}    #Please explain

Here this is similar parameter expansion, but here, we start from the right (%) and delete all characters up to, and including, the first / character in file leaving only the path information in dir. (makes sense)

done < <(exec grep -ZFlR "$str" "$dir")

Just feeds the loop as we discussed above.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

$file gets its values from read, which in turn reads from the input, which is redirected to at the end of the loop. <( ... ) is called "Process substitution", it basically behaves as a file whose contents is the output of the enclosed command. ## and % are instances of "Parameter expansion", they remove parts of the variable's value. You can search for all the terms and constructs in man bash.

choroba
  • 231,213
  • 25
  • 204
  • 289