0

My Aim --> Files Listing from a command has to be read line by line and be used as part of another command.

Description --> A command in linux returns

archive/Crow.java
archive/Kaka.java
mypmdhook.sh

which is stored in changed_files variable. I use the following while loop to read the files line by line and use it as part of a pmd command

while read each_file
do
        echo "Inside Loop --  $each_file"
done<$changed_files

I am new to writing shell script but my assumption was that the lines would've been separated in the loop and printed in each iteration but instead I get the following error --

mypmdhook.sh: 7: mypmdhook.sh: cannot open archive/Crow.java
archive/Kaka.java
mypmdhook.sh: No such file

Can you tell me how I can just get the value as a string and not as a file what is opened. By the way, the file does exist which made me feel even more confused.(and later use it inside a command). I'd be happy with any kind of answer that helps me understand and resolve this issue.

Naveen Dennis
  • 1,223
  • 3
  • 24
  • 39
  • possible duplicate of [Bash - How to pipe input to while loop and perserve variables after loop ends](http://stackoverflow.com/questions/19570413/bash-how-to-pipe-input-to-while-loop-and-perserve-variables-after-loop-ends) – MrTux Sep 24 '14 at 17:44

3 Answers3

1

Since you have data stored in a variable, use a "here string" instead of file redirection:

changed_files="archive/Crow.java
archive/Kaka.java
mypmdhook.sh"
while read each_file
do
        echo "Inside Loop --  $each_file"
done <<< "$changed_files"
Inside Loop --  archive/Crow.java
Inside Loop --  archive/Kaka.java
Inside Loop --  mypmdhook.sh

Extremely important to quote "$changed_files" in order to preserve the newlines, so the while-read loop works as you expect. A rule of thumb: always quote variables, unless you knows exactly why you want to leave the quotes off.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
0

Rather than storing command's output in a variable use while loop like this:

mycommand | while read -r each_file; do echo "Inside Loop -- $each_file"; done

If you're using BASH you can use process substitution:

while read -r each_file; do echo "Inside Loop -- $each_file"; done < <(mycommand)

btw your attempt of done<$changed_files will assume that changed_files represents a file.

anubhava
  • 761,203
  • 64
  • 569
  • 643
0

What happens here is that the value of your variable $changed_files is substituted into your command, and you get something like

while read each_file
do
        echo "Inside Loop -- $each_file"
done < archive/Crow.java 
archive/Kaka.java
mypmdhook.sh

then the shell tries to open the file for redirecting the input and obviously fails.

The point is that redirections (e.g. <, >, >>) in most cases accept filenames, but what you really need is to give the contents of the variable to the stdin. The most obvious way to do that is

echo $changed_files | while read each_file; do echo "Inside Loop -- $each_file"; done

You can also use the for loop instead of while read:

for each_file in $changed_files; do echo "inside Loop -- $each_file"; done

I prefer using while read ... if there is a chance that some filename may contain spaces, but in most cases for ... in will work for you.

afenster
  • 3,468
  • 19
  • 26