this one works
Actually it doesn't. The read will parse the line and rebuild it according to shell rules. For example,
echo ' hello world ' |
( read x; echo ">$x<" )
The captured $x has neither leading nor trailing space:
>hello world<
(Change read x to IFS= read -r x and compare the result.) Further, by not double-quoting the variable $x when you use it, it is subject to further processing. For example,
touch a b c # Create three empty files
x='here is my star: *'
echo "$x" # Outputs: here is my star: *
echo $x # Outputs: here is my star: a b c
In your case the files a, b, c will also be supplemented by the set of files you have in your current directory. (I should point out that touch only creates empty files as a side effect; if the files already exist it will simply update the last-modified time to now and will most definitely not truncate or recreate them.)
After all that, you won't be able to use the value of $x outside the loop as you've got the loop in a subshell. (Set x='demo' before the loop starts and then echo "$x" after the loop ends. It will have been unaffected by the loop.) You don't need the cat in this case as there's only one file:
while IFS= read -r x
do
printf '%s\n' "$x"
done <filename
But even after all that, typically there is little need to loop over input like this as many tools process files line by line anyway. c.f.
cat filename