Hacker News new | past | comments | ask | show | jobs | submit login

The fact that you have to use quoting nearly everywhere is a design flaw in the Borne shell. Some shells, like Plan 9's rc, for example, don't expand after variable substantiation. They have an operator to call if you want to explicitly force expansion. That's so much cleaner and less error prone.



Oil is Bourne compatible, but has a mode to opt you into the better behavior. Example:

    osh$ empty=''
    osh$ x='name with spaces.mp3'
This is like Bourne shell:

    $ argv $empty $x
    ['name', 'with', 'spaces.mp3']   # omit empty and split

    $ argv "$empty" "$x"       
    ['', 'name with spaces.mp3']     # unchanged
Opt into better behavior, also available with bin/oil:

    $ shopt --set oil:basic

    $ argv $empty $x                                                                                 
    ['', 'name with spaces.mp3']  # no splitting/elision
If you want to omit empty strings, you can use the maybe() function, which returns a 0 or 1 length array for SPLICING with @:

    $ argv @maybe(empty) "$x"
    ['name with spaces.mp3']      # omitted empty string
Example of splicing arrays:

    $ array=("foo $x" '1 2')

    $ argv $empty @array
    ['', 'foo name with spaces.mp3', '1 2']
This is called "Simple Word Evaluation": https://www.oilshell.org/release/latest/doc/simple-word-eval...

Feedback appreciated!

(Interestingly zsh also doesn't split words, but it silently removes empty strings).


> Interestingly zsh also doesn't split words

Should zsh users ever want that behavior they can enable it globally with the SH_WORD_SPLIT option, or more sensibly local to a given parameter with the = parameter expansion as in ${=var}. Also, it has the best comment in zsh's manpage: "SH_WORD_SPLIT [...] Note that this option has nothing to do with word splitting."

    $ x='name with spaces.mp3'
    $ print -l -- $=x  # "print -l" displays one element per line
    name
    with
    spaces.mp3
> it silently removes empty strings

You can keep the empty strings too if needed, but it requires using the @ expansion flag as in ${(@)arr}. It all becomes superbly readable, here I'll prove it:

    $ arr=($x '' 'old file')
    $ print -l -- "${(@D)arr:A:gs/old/new/}"
    ~/Desktop/name with spaces.mp3

    ~/Desktop/new file
In all seriousness, the zsh default handling feels right for interactive usage in this case. I'd love the strictness of oil, so that I simply don't need to remember these things. Not yet quite ready to give up on the zshexpn(1) goodies though.


OK interesting, didn't know about those options. I have seen the ${(...)x} syntax but not really used it. Yes it doesn't rate highly on my readability scale :)

I have heard the feedback that people like zsh expansion shortcuts, e.g. for globbing. Personally I am a find/xargs person, i.e. I select the files first with 'find' and then execute what I want with xargs.

It does require you to invert your thinking -- you're going from verb NOUNS to NOUNS verb. And you have to go back to the beginning of the command line and edit it. But I do find that it lets you test out the selection logic more naturally.

find can also be faster, e.g.

    find . -name .git -a -prune -o -print
skips the even STATTING .git directory, not just printing it, as opposed to ** I believe.

However find arguably has an even worse syntax (although it is explicit, just with some dumb shortcuts). One longstanding goal is to put a better syntax on the "evaluation model", which is pretty useful. It's basically a predicate that's evaluated over every node in the FS tree, but you can also customize the traversal.

It might be better for programs rather than interactively, but I started using it interactively too.

(When compiled with glibc, Oil also has bash/ksh-style extended globbing, which allows negation etc., but it seems only old scripts use that.)


You don't have to choose glob&loop or find&xargs as a zsh user, as there is a built-in zargs function:

   zargs -- $crazy_glob -- $command
Much like the `find | xargs` version you can begin with `zargs -- $glob` until your filter is correct and then tack the command on when you're ready.

Hmm, now I really like the idea of an oil-y version of this where you could do something like `oargs { $clearer_glob_DSL } { $command_block }`. It might even be possible right now using stest from dmenu¹ as a stand-in for a more advanced globbing alternative.

I believe part of the reason zsh users find the extended globbing functionality useful is the basic usage matches their expectations with other tools, unlike find's quirky syntax. The common filters use the same values as you'd see in `ls --classify` output. For example, you want executable files tack a `*` on or for sockets use a `=`.

FWIW, the find comparison isn't quite right. Out of the box `**/file` wouldn't traverse a .git directory anyway, unless the GLOB_DOTS option is set or you provide the equivalent flag as in `**/file(D)`. The less specific point is quite true though as adding negation and toggling flags in a single glob can be extremely difficult to read. See the examples at the bottom of zshexpn(1) for proof of that.

Edit to add: I hope this comes across as an attempt to be helpful as was intended, and not some awful stop motion attempt.

¹ https://tools.suckless.org/dmenu/


zsh is also doing the variable substitution better than bash. FYI, I just released rust_cmd_lib 1.0 recently, which can do variable substitution without any quotes: https://github.com/rust-shell-script/rust_cmd_lib


> The fact that you have to use quoting nearly everywhere is a design flaw in the Borne shell

I disagree. This can be considered a design flaw in other places, like filesystems that allow filenames with spaces and other idiotic complexity-inducing things.


Filenames with spaces only introduce complexity because bash was poorly designed. If you could treat strings with spaces the same as strings without spaces — as you can pretty much everywhere but the shell — then there wouldn’t be any additional complexity at all.


You have this point of view because you see filenames as data. Then, it wouldn't be nice to limit the contents of this data, thus filenames should be completely unrestricted. But there is another point of view. From the natural point of view of the shell, filenames are just identifiers. Like variable names in a programming language. Of course, there are programming languages that allow variable names with spaces, but they require weird quoting or a very limited syntax. Having variable names without spaces is so much convenient that it is a hard restriction in most languages. The same is true for the beautiful shell language, but unfortunately unix filesystems are poorly designed by allowing almost arbitrary filenames (note that null character and slash are not allowed).

I prefer to be able to do "for i in `ls`..." in my shell than to have filenames in my disk with hard spaces. This could be solved at the filesystem level, by a mount option (say, "-o cleannames") that exposes filenames with spaces using a non-breaking unicode space. You will take my simple shell one-liners that break with ugly filenames from my cold, dead hands.


Iterating a list of files should be handled by built in support for lists on language level and listing files from the stdlib, not based on your personal preference that spaces in strings is a universal delimiter for list-items because ls in some happy-cases happen to print files that way. What are you going to do next time you have to iterate any other type of data with spaces in it? Claim that's complex too?

Trying to shoehorn in arbitrary restrictions to data identifiers because of the way it's serialized in some situations just leads to the "csv-problem" where you never reach a uniform standard because some prefer using spaces as separators, some use tab, some use comma, semicolon, quotes are always allowed, etc...

Just define a standard array-type once and for all and use it to pass data for everything. Take python for example os.listdir() is just one call away, same for any other high level language. (I am aware bash already has this and one should use globs instead of ls, though i wouldn't advocate bash for anything regardless, for multiple other reasons)


that standard already exists and it is text identifiers separated by spaces




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: