Hacker News new | ask | show | jobs
by vbezhenar 2093 days ago
No, the reason is to aid human.

    $ touch 'a   b' c d
    $ ls
    'a   b'   c   d
Without quotes you would get something like

    $ ls
    a   b   c   d
and that would be confusing.

It wouldn't help shell scripts that assumed no whitespaces in file names anyway. You would get something like "'a", "b'", "c", "d" tokens which make no sense anyway.

The only sane way to iterate over files that I'm aware of is to use something like

    find . -type f -print0 | while read -d '' fname; do echo "fname: '$fname'"; done
But that's bashism...
4 comments

All this is horrible incidental complexity stemming from absolute madhouse insane splitting and substitution rules in shell languages.

Splitting on spaces, expanding asterisks, shell scripting is a minefield. You would basically never want any splitting to happen. Applications like reading a CSV by splitting using IFS are dirty hacks, that break with the slightest additional file parsing complexity (escapes, quoting etc).

In Python you can just do `os.listdir('.')` and it will actually do what you want. No 5 layers of "oh, actually" and "yes, but what if", which inevitably happen in threads discussing the simplest shell operations. It just works as intended. If you only want the files and not the directories, you can do `filter(os.path.isfile, os.listdir('.'))`.

There is no reason to use shell scripts for anything more complex than a few lines or for one-off interactive work.

To be even more pedantic, you actually want to do

    while read -d '' fname
    do
      echo "fname: $fname"
    done <<<"$(find . -type f -print0)"
Because if you wanted to export a variable from the loop body, it wouldn't work in your example -- the loop is running in a subshell when you pipe output into it. <<<"$(...)" is not exactly the same thing (it doesn't stream input) but it does allow you to get around this problem. For folks who suggested glob expansion -- that's okay for simple file listings, but find lets you do more complicated ones that can be quite useful. Not to mention that the suggested

    for fname in *; ...
Is not actually doing the same thing -- find lists files recursively.
find(1) is super useful for any hierarchical or filtering case, and I use it often, but it can also be arcane and unwieldy. In many circumstances your cwd shell iterator can be simpler:

    for fname in *; do echo "$fname"; done
This skips dotfiles, of course, but that’s intentional. The point of dotfiles is to be skipped.

With bash or on GNU systems try

    printf "%q\n" "$fname"
instead of echo, to obtain an escaped string.
I thought that it would iterate over space-separated tokens. But it works, some magic here, thanks, that's definitely the proper way to go.
It's because the result of a glob is not immediately subjected to shell expansion.
won't this echo "*" if there are no files in the directory?

I always have to look up the 'safe iteration' invocation when iterating files in a directory, because it involves jumping through a few more hoops than is really reasonable.

Yes. You can change the behaviour in bash by setting the nullglob option (shopt -s nullglob).

    find -exec

?
Won't work for complex loop body.