bash/zsh input process substitution gives syntax error in conjunction with while - bash

These work fine and do what they should (print the contents of the file foo):
cat <foo
while read line; do echo $line; done <foo
cat <(cat foo)
However this gives me a syntax error in zsh:
zsh$ while read line; do echo $line; done <(cat foo)
zsh: parse error near `<(cat foo)'
and bash:
bash$ while read line; do echo $line; done <(cat foo)
bash: syntax error near unexpected token `<(cat foo)'
Does anybody know the reason and maybe a workaround?
Note: This is obviously a toy example. In the real code I need the body of the while loop to be executed in the main shell process, so I can't just use
cat foo | while read line; do echo $line; done

You need to redirect the process substitution into the while loop:
You wrote
while read line; do echo $line; done <(cat foo)
You need
while read line; do echo $line; done < <(cat foo)
# ...................................^
Treat a process substitution like a filename.

bash/zsh replaces <(cat foo) by a pipe (kind of file) having a name as /dev/fd/n where n is the file descriptor (number).
You can check the pipe name using the command echo <(cat foo).
As you may know, bash/zsh also runs the command cat foo in another process. The output of this second process is written to that named pipe.
without process substitution:
while ... do ... done inputfile #error
while ... do ... done < inputfile #correct
same rules using process substitution:
while ... do ... done <(cat foo) #error
while ... do ... done < <(cat foo) #correct
Alternative:
cat foo >3 & while read line; do echo $line; done <3;

I can suggest only workaround like this:
theproc() { for((i=0;i<5;++i)) do echo $i; }
while read line ; do echo $line ; done <<<"$(theproc)"

Related

Read from a file and stdin in Bash

I would like to know if I can write a shell script that accepts two arguments simultaneously, one from a file and the another one from stdin. Could you give some example please?.
I trying
while read line
do
echo "$line"
done < "${1}" < "{/dev/stdin}"
But this does not work.
You can use cat - or cat /dev/stdin:
while read line; do
# your code
done < <(cat "$1" -)
or
while read line; do
# your code
done < <(cat "$1" /dev/stdin)
or, if you want to read from all files passed through command line as well as stdin, you could do this:
while read line; do
# your code
done < <(cat "$#" /dev/stdin)
See also:
How to read from a file or stdin in Bash?
This topic seems to be helpful here:
{ cat $1; cat; } | while read line
do
echo "$line"
done
Or just
cat $1
cat
if all you're doing is printing the content

Why is "while read -r a" command unable to read string that is not newline terminated?

Here is my shell script.
printf "foo" | (read -r a; echo $a)
printf "bar" | while read -r a
do
echo $a
done
Here is the output.
$ sh foo.sh
foo
My question is: Why is foo printed but bar not printed? When read -r a is able to read a string that is not newline-terminated why is it unable to do so when it used as a condition of a while loop?

What does the "done < $var" at the end of a loop do?

Just a simple question - I'm wondering what the following code is doing:
nlwd="$PWD/NLWD.txt"
cat /dev/null > $nlwd
echo "Enter filename to process:"
read name
while read line
do
uid="$(echo $line | cut -d, -f1)"
echo "$uid" | grep [0-9] >> $nlwd
done < $name
In particular, I'm wondering what the done < $name is doing.
It's taking a file name, reading that file line-by-line, and doing stuff with each line.
< is an input redirect, which means that the loop is taking its input from $name.
For example:
while read LINE
do
echo $LINE
done < $name
...is essentially the same as:
cat $name
In response to your comment, the cat /dev/null > $nlwd just empties out the file's contents. This time, it uses the > output redirection to take the contents of /dev/null (which is Linux's black hole file), and outputs that emptiness into file represented by the $nlwd variable. Here's a simpler example:
$> echo "something" > something.txt
$> cat something.txt
something
$> cat /dev/null > something.txt
$> cat something.txt
$>
Further reading: http://en.wikipedia.org/wiki//dev/null
It's an input redirection. The while loop (and thus each command in the while loop, specifically read) will take its standard input from the file named by $name.

Skip line in text file which starts with '#' via KornShell (ksh)

I am trying to write a script which reads a text file and saves each line to a string. I would also like the script to skip any lines which start with a hash symbol. Any suggestions?
You should not leave skipping lines to ksh. E.g. do this:
grep -v '^#' INPUTFILE | while IFS="" read line ; do echo $line ; done
And instead of the echo part do whatever you want.
Or if ksh does not support this syntax:
grep -v '^#' INPUTFILE > tmpfile
while IFS="" read line ; do echo $line ; done < tmpfile
rm tmpfile
while read -r line; do
[[ "$line" = *( )#* ]] && continue
# do something with "$line"
done < filename
look for "File Name Patterns" or "File Name Generation" in the ksh man page.

echo from lines of a file

i have a file "myfile.txt" that have the next content:
hola mundo
hello word
and i want work with every line
for i in `cat myfile.txt`; do echo $i; done
i hope this give me
hola mundo
hello word
firts one line, then the other, but get
hola
mundo
hello
word
as I can demanding results until newline instead of each space?
ty all
That's better
cat myfile.txt | while read line; do
echo "$line"
done
or even better (doesn't launch other processes such as a subshell and cat):
while read line; do
echo "$line"
done < myfile.txt
If you prefer oneliners, it's obviously
while read line; do echo "$line"; done < myfile.txt

Resources