0

I am practising to use the command read:

$ ls * | while read line; do printf "test %s\n"  $line; done
test data1.txt
test data5.txt
test data6.txt
test data7.txt

It works properly, but

$ ls * | while read line; do file $line; done
data1.txt: cannot open `data1.txt' (No such file or directory)
data5.txt: cannot open `data5.txt' (No such file or directory)
data6.txt: cannot open `data6.txt' (No such file or directory)
data7.txt: cannot open `data7.txt' (No such file or directory)
data8.txt: cannot open `data8.txt' (No such file or directory)

The file names are legal

$ ls
data1.txt  data5.txt  data6.txt  data7.txt  data8.txt

What's the problem with my code.

kvantour
  • 25,269
  • 4
  • 47
  • 72
AbstProcDo
  • 19,953
  • 19
  • 81
  • 138
  • 4
    I can't think of any good reason why you wouldn't use `find` to do this and avoid, at all costs, piping `ls` which will only lead to pain and suffering. – JNevill Nov 07 '18 at 14:23
  • I want to explore `read` @JNevill – AbstProcDo Nov 07 '18 at 14:24
  • 2
    @JNevill One good reason: `find` is a overkill for this job. `file *` will do. – Socowi Nov 07 '18 at 14:25
  • 4
    I would explore it with something other than `ls` as an input. `ls` output is terribly fragile and is not going to be a good learning tool. At any rate though, I'm not able to replicate the issues you are facing on your second example. It works fine for me. – JNevill Nov 07 '18 at 14:26
  • 6
    Well, your filenamess have any unprintable characters, like whitespaces, tabs or newlines? Can't reproduce: `touch data{1,5,6,7,8}.txt; ls data*.txt | while read line; do file $line; done` (or pro `find data*.txt -maxdepth 1 -type f -print0 | while IFS= read -d '' -r line; do file $line; done`) – KamilCuk Nov 07 '18 at 14:26
  • How were `dataX.txt` created? – Alex M Nov 07 '18 at 14:40
  • Are you showing the exact output in each case? `ls *` is going to show you the content of subdirectories, which `file` will fail to find. – borrible Nov 07 '18 at 15:44
  • 2
    Never parse `ls` : http://mywiki.wooledge.org/ParsingLs – kvantour Nov 07 '18 at 16:02
  • Can you run ones `ls --quoting-style=shell-escape` and show us the output – kvantour Nov 07 '18 at 17:15
  • See [Read a file line by line assigning the value to a variable](https://stackoverflow.com/q/10929453/4154375) and [BashFAQ/001 (How can I read a file ... line-by-line ...?)](https://mywiki.wooledge.org/BashFAQ/001). – pjh Nov 07 '18 at 17:56

1 Answers1

3

My suspicion is that your files are not called "data1.txt" but actually something more like "data1.txt ". Notice the subtle space at the end. This could easily be the result of a tiny typo in a script or so. It could also be the usage of unprintable characters. You can check this with ls --quoting-style=shell-escape.

Example:

$ touch "data1.txt "
$ touch "data2.txt"
$ echo "a" > "data3.txt "
$ ls data* | while read line; do file $line; done
data1.txt: cannot open (No such file or directory)
data2.txt: empty
data3.txt: cannot open (No such file or directory)

         ^
Notice the missing spaces

There are several problems with this single line:

  1. Never parse ls
  2. Always quote your variables
  3. read removes all leading and trailing whitespace characters. This is actualy the main problem here

So, if you want to solve the read problem, you do:

$ ls data* | while IFS= read -r line; do file "$line"; done
data1.txt : empty
data2.txt: empty
data3.txt : ASCII text

         ^
Notice the spaces, this is due to read

But the proper way to do it would be:

$ for f in data*; do file "$f"; done
data1.txt : empty
data2.txt: empty
data3.txt : ASCII text
kvantour
  • 25,269
  • 4
  • 47
  • 72