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).
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 stringsYou 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.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.)
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.
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.
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.
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)