The shell and its crappy handling of whitespace
blog.plover.com
blog.plover.com
I find moving to even Python to be an inadequate answer, because the shell does two crucial things very very well - it manipulates the filesystem and runs commands in their most native format. And even Python is very cumbersome at those two tasks.
But sometimes you need good data structures/execution flow control, and good filesystem/command control, at the same time.
Intuitive, consistent, predictable whitespace handling would fix a lot of shell scripting problems, though.
(I haven’t given Powershell a serious shot, maybe I should.)
You can call pure python plus it has the simple running of command like other shells.
I currently use zsh and quickly drop into IPython when needed.
x = `{echo hi there: $user} # evalutates to list ('hi' 'there:' 'ori')
y = `:{echo hi there: $user} # evalutates to list ('hi there' ' ori')
There's no other word splitting, so: args = ('a' 'b c' 'd e f')
echo $#args
echo $args(3)
will print: 3
d e f
The shell itself is pleasantly simple; there's not much to learn[2]. And while it's not fun for interactive use on unix because it offloads too much of the interactive pleasantness to the plan 9 window system (rio), it's still great for scripting.In Perl, you can use backticks to execute commands for example
The globbing support dir has is wonderful
btw csh/tcsh "" quoting rules are atrocious, avoid it..
Thankfully Perl exists.
You can make a helper function or commandlet of course, but it obfuscates the code and is a pain to distribute around always where needed. They argue that non-zero exit codes for errors in external programs is a convention that doesn't exist consistently where Powershell is used. OK, but all the programs I use do follow that convention and so I find that very painful.
PowerShell's default handling for native commands is treating anything written to stderr as signaling an error, which is somewhat more reliable for old DOS/Windows commands (but not necessarily reliable for some Unix commands that use stderr as a secondary logger for various reasons including because they expect stdout to pipe redirected more often than not). Though you need to set $ErrorActionPreference [0] to something other than "Continue" to notice this behavior.
However, since Powershell 7.3 there is also a setting you can temporarily change to treat non-zero returns as errors: $PSNativeCommandUseErrorActionPreference [1]. Note that as its name suggests you need to set $ErrorActionPreference to something useful for it to work as you intend.
[0] https://learn.microsoft.com/en-us/powershell/module/microsof...
[1] https://learn.microsoft.com/en-us/powershell/module/microsof...
All it takes to understand it is that it took a while for anyone to consider that spaces in filenames was (or might be) a thing.
I don't know that this is true, but having started on Unix about the same time as the author of TFA, I know that I found it quite disorienting when I first started interacting with Windows/macOS users who regularly used spaces in their filenames and thought nothing of it.
I suspect this wasn't an "error" as much as just a failure to grok the pretty basic idea that "bite me.jpg" is an entirely valid filename, and maybe more useful than "biteme.jpg" or "bite_me.jpg"
Course not have a hard separation between paths and file names is also not good.
./folder_what/folder_x/folder_zee:file_name.whatever
The above would be betterAlso please for the love of god do not use peoples names as file names because they are not unique and they change oftener than you think.
Whatever the concern, the ship has long since sailed on spaces in filenames, it's not going to be undone, and we are obligated to handle them.
If it's on the keyboard, they expect to be able to use it.
I don't want CR in file names, but someone somewhere will want it for some strange formatting reason (I've seen things).
> A file name is not a document's title.
You say that, other people have other ideas. When you let non-technical people use your too technical system, it will crash.
---
A software tester walks into a bar.
Runs into a bar.
Crawls into a bar.
Dances into a bar.
Flies into a bar.
Jumps into a bar.
And orders:
a beer.
2 beers.
0 beers.
99999999 beers.
a lizard in a beer glass.
-1 beer.
"qwertyuiop" beers.
Testing complete.
A real customer walks into the bar and asks where the bathroom is.
The bar goes up in flames.
2, Never the less, people do actually expect to use even that.
And they're not even wrong. It's not their fault that it breaks things.
In Windows they get away with it beacause for most users there is no such thing as a command line which is actually at least 2 little programming languages combined with special syntax (the shell itself and readline, and maybe even the tty line discipline and stty settings count as yet a 3rd little dsl in there, maybe even the terminal emulation counts as yet another!).
All input, including entering filenames, even when you have to type them, goes through fully managed input widgets that allow almost everything, and they catch and reject the few things they must right on input.
I've written things that take user input and url-encodes it for the matching filename myself, so that the user can do whatever they want, yet the the filename remains convenient and safe, yet still directly human legible & meaningful unlike base64 or random hashes.
That interface with I guess no less than 4 different layers of special syntax which is a shell, is useful but it's really not something to expect anyone but a programmer or technician to have to deal with. All the cases where a "bad" filename breaks something are just examples of the application's internals leaking out and being exposed to the user, which was always wrong since that's also the basis of all security exploits.
It's like taking that urlencoded data I mentioned and dropping it directly into printf, segfault, and complaining that the user should know better than to include %d in a text input.
At least most Windows users are "trained" by the Windows that asterisk, question mark, vertical bar, double quote, prefix and suffix spaces "aren't valid characters for files" (in a weird way, it's a Windows limitation, not a NTFS one). I expect only the worst (case insensitive stuff) naming schemes when the files comes from a macOS user.
Let's see what he says in this article:
- "$* is literally useless": no, it's used to covert arguments to a string separated by the first char in IFS. Useful pretty much only for logging, but sometimes joining strings. $@ is POSIX and should be used in all other cases. A niche feature that generally shouldn't be used isn't an issue in shell
- $* and $@ are the same except when $@ is quoted: true, except in more modern shells like bash, where $@ is an array, not a specially-treated variable. I don't know who told him to use $* for 35 years, but the author should be mad at them instead
- To make it work properly you have to say `for i in ; do cp "$i" /tmp; done`: wrong. cp can't distinguish arguments and flags. You must either use `./` or `cp -t /tmp -- "$i"` (or some variation). It's correct in the glob/wordsplit sense, but saying this is proper is incorrect
- "And the shell doesn't have this behavior for any other sort of special character": He doesnt even mention why spaces are magic. Hint: they aren't. It's entirely controlled by IFS, which you can change. The default is space, tab, and newline. He also doesn't mention globbing, which can arguably be more disasterous.
An article about wordsplitting and he doesn't even mention it once? This is at best a rant
Do yourself a favor and read Greg's entire wiki: https://mywiki.wooledge.org/BashFAQ and just learn how to use it properly, and then you can complain about how painful it is to learn or how easy it is to use incorrectly rather than how bad it is if you use it wrong.
I am grateful for all those who historically came up with the concepts and put in the work, but if anybody were to design a text interface to computers where things can be piped into other commands etc. today they could heavily improve the usability of the thing and safe thousands of collective hours wasted. And peobably like when trying to create a sucessor to email, nobody would use it.
Thank god for shellcheck. It can yell at everyone for me
> A command enclosed in parentheses preceded by a dollar sign, like `$(...)', or quoted with grave accents, like ``...`', is replaced with its standard output, with any trailing newlines deleted. If the substitution is not enclosed in double quotes, the output is broken into words using the IFS parameter. https://www.csse.uwa.edu.au/programming/linux/zsh-doc/zsh_15...
`vidir [path]` will open an editor with the given directory as buffer contents. Editing and saving will translate to a sequence of `mv` invocations.
Requiring filenames to be quotes if they contain spaces, or optionally otherwise, would help – similar to CSV values with commas in. Though this opens up other issues: what about filenames containing quotes, and when nesting calls² the question of what part does the unpacking becomes a complex question without explicit structure. And to be honest we have enough trouble with clients sending us malformed CSV with unquoted commas that I can confidently state this wouldn't work either.
And you can't trust users to follow instructions, even internal users, where the limitation might be an issue. If you put a simple helper tool together and say “it won't work with spaces in filenames”, either it becomes a high priority ticket that the thing you jammed together as a favour doesn't work with spaces, or you get so sick or responding to problem reports that turn out to be due to the matter that you spend time fixing it up anyway. </rant>
--
[0] for systems that pass structured data, such as powershell, spaces are handled fine, though that would be a lot of work to retrofit into shells & utilities with a generally useful level of conformance
[1] like the convergent evolution of wings in pterodactyls/birds/bats or crab-like features all over the place
[2] for instance ssh to run a complex command line involving sudo
See https://drewdevault.com/2023/07/31/The-rc-shell-and-whitespa... to see the examples work out of the box as you woula naturally write them.
Not really, bash, fish and zsh all have arrays as "first-class data structures"; it's only really a problem if you really want to limit yourself to POSIX shell syntax.
Get-Process | Out-Host -Paging | Format-List
Case sensitive hyphenated compound word commands?
It is idiomatic to capitalize liberally though.
That's actually something I have come to appreciate about Powershell is that idiomatic distinction concept that scripts are more likely to be read by other people (including Future You with no memory of having written the script in the first place) and to write "full sentences" for an audience, but feel free to be fast and loose in the REPL all you want.
> Case sensitive
they're not
Also your command is equivalent to:
ps | oh -paging | fl
File path names are bytestrings on most OSes, so you have undefined behaviour if your pipeline can't handle that.
"just don't mangle my filenames" is a pretty basic requirement for a universal use shell.
Powershell's structured data can cause issues if you expect it to be similar to Unix-like shells.
For example, Powershell mangles piped binary data by default:
https://brianreiter.org/2010/01/29/powershells-object-pipeli...
Correct handling of piped binary data requires additional flags:
https://stackoverflow.com/questions/54086430/how-to-pipe-bin...
Exactly. It means that system administrators need to be aware of the differences when working with Powershell versus other shells to avoid silent corruption of binary data.
Powershell is cross-platform and runs on Linux and MacOS as well as Windows:
https://github.com/PowerShell/PowerShell
You can discover differences between Powershell and traditional shells as a Linux admin.
And Python can cause issues if you expect it to be similar to Java. They're 2 different tools, so if you expect it to behave similarly to bash, that's on you
Is this a No True Scotsman?
I'd counter that there are a couple of differing levels of validity:
1. Printable ASCII alphanumerics, period, hyphen, underscore. Guaranteed to be portable anywhere and completely safe.
2. Safe ASCII punctuation (space, ~, $, etc). Usable on virtually all systems, likely to cause weird behavior in poorly written shell scripts.
3. Unsafe ASCII punctuation (< > : " \ | ? *). Disallowed on Windows, also likely to cause weird behavior when allowed.
4. Invalid UTF-8 sequences. Disallowed on macOS, not representable at all on Windows (which uses UTF-16 for filenames!), and will cause havoc in some programming languages like Python which assume that the filesystem is Unicode.
5. ASCII control characters (0x01 - 0x1e). Also disallowed on Windows, and you'd have to be nuts to use them even if they are supported.
IMO it's better to just avoid file systems altogether if you can and put everything in a database.
POSIX is not the same as "portable", certainly not "universally portable". POSIX shell scripts should be written to handle the capabilities of POSIX systems. Likewise scripts for POSIX-compliant shells, like Bash scripts. Non-POSIX shells may impose whatever restrictions they like, e.g. Windows disallows the file names CON, PRN, AUX, NUL, COM0 through COM9, and LPT0 through LPT9. Those are in your category 1 but are definitely not safe on Windows.
When I write bash scripts which will go to git and probably stay there for a while, I use long form of the switches, like --long-option-name instead of -lon. They make the script more readable when after a year someone wants to modify it.
It allows things like history | fzf to work very well.
There are some things shell does really well.
There is a very simple way to write a breadth-first webcrawler that works in stages and saves the frontier in an ordinary text file, this kind of webcrawler can be implemented in almost any programming language, particularly shell. That is, you write a script that loops over the urls in the frontier, fetches them, then writes new urls it find in the next frontier, you can use sort, uniq and such to manage the frontier.
That simple breadth first webcrawler has the big advantage that it does not get stuck in web traps (say a calendar that has a button to go the next year which could easily scan up to the year 9595, if man is still alive). Or rather, after a certain number of passes (maybe N=10-15] all the real URLs have been crawled and the remaining URLs are web traps.
In a language like Java it is tempting to write a much more complex crawl manager that has the potential to crawl faster with threads but takes more care to not crash target sites, get caught in web traps, etc. The first web crawler I wrote (1999) had a highly complex crawl control system, future web crawlers I wrote got simpler and simpler because the simple breadth first crawler works very well for small projects.
It was actually just a simple act of crawling over source files! Amazon was looking to remove their customer support phone numbers from all web pages (to save money, they wanted people to stop calling them; back in the early 2010s)
I share the wonder that it took several days (perhaps that is the anecdote taking a life of its own, perhaps it was actually several hours. Or maybe it was was several days interspersed with on-call support, meetings, and other work running in parallel).
Though.. the file parsing libraries back in the early 2010s in Java are somewhat basic, probably using Java 5. Which does make me wonder, how long would that exercise take in Java 8 or later compared to Java 5?
Here are some facts for you to ponder:
1. Our Contact Reduction team really did have exactly this problem in 2003. This isn't a made-up example.
2. Someone on our team produced the list within an hour, and the list supported more than just the 2 formats above.
3. About 25% to 35% of all software development engineer candidates, independent of experience level, cannot solve this problem, even given the entire interview hour and lots of hints.
[0] https://sites.google.com/site/steveyegge2/five-essential-pho...
http://www.leancrew.com/all-this/2011/12/more-shell-less-egg...
https://buttondown.email/hillelwayne/archive/donald-knuth-was-framed/
McIlroy was asked to show off productivity in the newly popular Unix system, and he did that.While often framed as "a contest" the two approaches are really solving different enough problems as being apples-oranges. So, the only meaningful judgement of a contest is "who better achieved their narrow goals?"
I've found in discussions with people that they seem unable to separate their judgement of "a winner" from their own personal weight on the value of the two narrow goals ("detailed articulation mixing code & text" vs. "just getting shit done").
There is probably some broader lesson about people in that (and indeed probably a specific term in the science of psychology) - those who really like apples or really like oranges being inclined to force a non-contest into one of their favored "Eat oranges!" rhetorical moves.
EDIT: and it's actually even more subtle - the very same person can side with either depending on broader context or framing. There are those who would praise detailed documentation over write-only hacks requiring detailed memory of tool innards in one situation yet decry it as a waste of time in another. (All part of why survey results are not to be accepted naively.)
The macOS API is really Foundation - a layer above POSIX - if you use that then the filesystem is not that bad. It gives another solution to meta data like .DS_Store which is bundles. Bundles are an object in the Foundation API which you manipulate as one - on the filesystem they are a directory. Think of a bundle as a zipfile that you can look at using POSIX tools.
The files being in odd places is for similar reasons to XDG"s attempt at cleaning up Unix. I think that the MacOS/NeXT structure predates XDG.
What does it mean to work with whitespace properly?
When commands are entered at the command line and a shell is parsing, how do you distinguish between whitespace between commands, command parameters and switches, filenames that include whitespace characters, paths that include whitespace characters, etc. ?
It is a difficult question that does not have good answers.
Autocompletion works well enough for doing things like "ls file with space": either just as "ls file<Tab>" or "ls 'file<Tab" (in zsh it picks up on a starting ' or " so you don't end up with loads of backslashes).
OPTS="-a -b -c"
foo $OPTSI'm not sure about rc on this as I'm not too familiar with rc, but I'd be surprised if there wasn't a way to do this.
A problem with that: What is 'properly'? The current solution is one take on how to do it properly.
We know how. Using structure beyond a line of text. The problem with that is then your shell and all the command you run through it need to agree on that structure and implement it.
PowerShell works this way, though has a few of its own gotchas (mangling binary data if you aren't careful being one of them). A couple of shells for unix-a-like systems do to, but it falls apart when you start calling de-facto standard tools that are not retro-fitted to support accepting and outputting that structure.
Once you have decided on the “proper way” you'll find that was the easy part and getting that way implemented in enough places that it is useful to be a somewhat Sisyphean task.
IMO the only way to do spaces in filenames properly without an impractically radical change to all your tooling, is to consider spaces in filenames to be a bug. But good luck getting the world to accept that!
That is actually the best policy. Case sensitivity is unnatural and weird; we don’t think by default of readme, Readme, and README as being 3 different things. But then, when the user chose a file’s name, it’s better if the file actually has that name, and keeps it consistently regardless of how the user interacts with the file system (hence the case-preserving). The case-preserving aspect has no effect on anything outside the kernel. For all intent and purposes, it is case insensitive for any userspace code.
I believe you don't, but I certainly do.
Case sensitivity is naive and simple, case-insensitive systems are complex and require Case Mappings [1] and Normalization [2] and those are ugly, unless you want a half-assed system that is case-insensitive only when you only use ASCII.
[1] https://unicode-org.github.io/icu/userguide/transforms/casem...
> There are different case mappings for different locales. For instance, unlike English, the character Latin small letter ‘i’ in Turkish has an equivalent Latin capital letter ‘I’ with dot above ( \u0130 ‘İ’).
> In general:
> case mapping can change the number of code points and/or code units of a string,
> is language-sensitive (results may differ depending on language), and
> is context-sensitive (a character in the input string may map differently depending on surrounding characters).
[2] https://unicode-org.github.io/icu/userguide/transforms/norma...
In my dream world it would enable changing "The shell and its crappy handling of whitespace _ Hacker News.html" to "The-shell-and-its-crappy-handling-of-whitespace_Hacker-News.html"
The real solution is something that does not split a variable on spaces, or that offers some control about how it splits things. These exist, there is no excuse to stick to bash in this day and age.
We really don’t need another layer of obfuscation between GUIs and the underlying file system.
First off, no, that's not a bug. The alternative is some variant of "here's a data structure to hold the list of arguments you need to manipulate manually". And that's how program execution works in "real programming language" environments (even things like PowerShell).
And it sucks, which is why we all continue to use the Bourne shell after four decades to solve these problems. What we really want is "here's a string to execute just like I typed it; go!". Sure, that's not what we think we want. And we might convince ourselves that mishandling based on that metaphor is a bad thing and write too-smart-for-our-own-good blog posts about it to show how smart we are.
But no, we don't want that. We want shell. If we didn't want shell, we wouldn't write shell scripts. And yet, here we are.
Again, four decades outweighs any amount of pontification in a blog post. If you aren't willing to start from a perspective of why this system remains so successful, you're probably not bringing as much insight to the problem as you think you are.
Many more people used MS-DOS/Windows during that time.
>And it sucks, which is why we all continue to use the Bourne shell after four decades to solve these problems.
>But no, we don't want that. We want shell.
People continue to use Linux and Unix and interactive shell for good reasons. Shell as scripting shell is there simply because it's available, a form of Stockholm Syndrome I guess.
Yeah, here’s a strong culture of writing ultraportable scripts in case our precious script ends up running on a SunOS box somehow. There are cases where it’s important, but not the dozens of small-ish scripts we end up relying on daily.
I entirely agree about the rest of your comment.
But not since. No one writes new win16 or DOS or batch files. People still write shell scripts. Every day. Despite the existence of a deluge of supposedly better solutions. And if your explanation for why shell is so terrible doesn't have a way to explain that, I argue it's an incorrect explanation.
"We all have Stockholm Syndrome" is just ridiculous, btw.
More than you'd think (CMD files are still supported). Fortunately the syntax is too basic for it to be used too much. However, EXCEL+VBScript runs the financial world. Is VBScript a good language? Shell and VBScript have some half-decent insights for their time, but they have remained stuck while programming languages have improved. The reason people write less CMD files - because Microsoft improved with Powershell.
Most of them suffer for a decade, then (if they stay under Windows) they mostly find Python or Autohotkey.
function backup {
local SOURCE=$1
local DESTINATION=$2
local ID_RSA=$3
# VALID_SSH is hard-coded for testing.
local VALID_SSH="yes"
local REMOTE_SHELL=""
if [[ "${VALID_SSH}" == "yes" ]]; then
REMOTE_SHELL='-e "ssh -i '${ID_RSA}'"'
fi
rsync -av \
--no-perms \
--links \
$(if [[ "${VALID_SSH}" == "yes" ]]; then echo "${REMOTE_SHELL}"; fi) \
"${SOURCE}" "${DESTINATION}"
}
As it is when calling the function: backup "." "backup@redstar.local:" "id_rsa"
rsync complains with: Missing trailing-" in remote-shell command.
rsync error: syntax or usage error (code 1) at main.c(438) [sender=3.1.3]
But if I echo the `rsync` instead of calling it, its output is perfectly valid it actually
runs as expected: rsync -av --no-perms --links -e "ssh -i id_rsa" . backup@redstar.local:
Been fiddling yesterday and today with no luck, it must be something very subtle. Tried a bunch of different things
from SO suggestions but none seem to work. Meh. REMOTE_SHELL="-e \"ssh -i ${ID_RSA}\""
already? $(if [[ "${VALID_SSH}" == "yes" ]]; then echo "${REMOTE_SHELL}"; fi) \
Change that to $(if [[ "${VALID_SSH}" == "yes" ]]; then echo ${REMOTE_SHELL}; fi) \
and have REMOTE_SHELL="-e \"ssh -i '${ID_RSA}'\"" rsync -av --no-perms --links "ssh -i id_rsa" . backup@redstar.local:
But, you can re-insert it in the if with: $(if [[ "${VALID_SSH}" == "yes" ]]; then echo -e "\055e ${REMOTE_SHELL}"; fi) \
and removing the "-e" from the REMOTE_SHELL var. Then... Same error as before! REMOTE_SHELL=""
when it doesn't want to get used, and then inserting it every time, instead of using the conditional rsync ... ${REMOTE_SHELL} ...
should also work. If it's empty, then it won't add anything, but if there's something then it'll get passed in. function backup {
local SOURCE=$1
local DESTINATION=$2
local ID_RSA=$3
local VALID_SSH="yes"
local RSYNC_OPTIONS=(-av --no-perms --links)
if [[ "${VALID_SSH}" == "yes" ]]; then
RSYNC_OPTIONS+=(-e "ssh -i ${ID_RSA}")
fi
rsync "${RSYNC_OPTIONS[@]}" "${SOURCE}" "${DESTINATION}"
}
I got lucky and found this information with an explanation at: https://www.mail-archive.com/search?l=bug-bash@gnu.org&q=sub...So, is it a good advice to always use bash arrays to construct arbitrary options for commands?
But your main problem is that `REMOTE_SHELL` is a string, when you need an array. Though I suppose you could make it a string if you used `eval` around its whole use.
RSYNC_ARGS='--progress --human-readable --human-readable'
if some_condition; then RSYNC_ARGS="$RSYNC_ARGS --exclude-from=some_file"; fi
rsync $RSYNC_ARGS foo bar
I can't think of a use for $* though and would guess it probably existed before $@ and is still there just for backwards compatibility.In Bash you can use arrays though:
args+=('--progress=yes please')The array style probably should always be used nowadays (don't forget you also need to insert it into the command like ${args[@]}), but I learned before it was common (possibly before it was a thing, I'm having trouble finding exactly when basic arrays were added to bash) so I still use the string style when I know I won't need it.
More and more I find that confidence is one of the best goals you can have in every software and system project, and any tool or process that can increase your confidence is going to make your life easier. Shellcheck gives you that confidence when working with the shell. Never write shell code without it.
The first "wrong" example in this article for example (cp $i /tmp) is perfectly fine in zsh, and the complex 'mv "$i" "$(suf "$i")".jpg' is just 'mv $i $i:r.jpg' in zsh.
$* and $@ are identical in zsh, and both are also arrays. There is no way to use it wrong really, and no weird '"$@" with quotes is a special case that's handled different from everything else'.
Most "shell problems" people experience are "bash problems" (or "POSIX sh problems", but for many they're more or less synonymous); certainly pretty much all the problems listed in this article are.
This always proves to be a controversial opinion, but I will forever keep insisting: just don't use POSIX sh or bash if it can at all be helped. You most likely don't need the portability, and installing zsh (or fish, or oil, or whatever) is easy enough in most the cases because you're the one controlling the environment.
no such problems in bash.
This must be something you configured, or added in a plugin or something, because it's not the default behaviour, and AFAIK it's also not a simple setting.
You choose to expand a variable and then complain about not wanting the expansion?
Or about the behaviour of clearly documented special variables doing exactly what they're supposed to instead of something another special variable does? What are they unhappy about exactly, they don't like the symbol that was chosen??
These are all things you find out within minutes of reading the manpage. (one of the best written manpages out there, in fact).
Heck, this isn't even a "cannot whitespace" problem. It's expanded vs unexpanded tokens. Which is a feature of the language. Because it's text/stream based (not "despite"). 35 years of shell experience? I call bait.
I also agree that `mv "$i" "$(suf "$i")" is unnecessarily cumbersome.
The thing about spaces (and expansion more generally) as I understand it is this. At some point the bash designers had a choice how to represent expanded, unexpanded, and verbatim strings. One could argue that verbatim should have been the default, with expanded using single quotes, and unexpanded using double quotes, such that you would only get expansion if you really wanted it. This might have been my choice if I had designed it.
I'm guessing, however, that the designers quite reasonable went for the opposite logic, which was, if you use a $ in your string, you're probably after expansion, rather than a verbatim $ symbol (and you can always escape it in the unlikely scenario that you did want a verbatim one). So presumably they chose expanded as the default because it would be the most common, leaving single quotes for verbatim instead.
Curious to read this article when it recovers from the HN load.
Joking aside, as a relatively newbie to the star nix world and liking it - shell scripting in Linux/Unix is broken.
The object handling approach that powershell provides just seem to be a better approach.
Bell Labs fixed it with the shell called “tc”. They can’t be blamed that you ignored it.
> mv "$i" $(suf "$i").jpg # two sets of quotes
> done
Doesn't this need another set of quotes, `mv "$i" "$(suf "$i")".jpg`, or else it'll just fail in a slightly different way when asked to `mv "bite me" bite me.jpg`?
$ echo $(echo "a b")
a b
$ echo "$(echo "a b")"
a bYou have my sympathy, it is super embarrassing when it happens.
* That article was #4 in a series. I did add a correction to article 3 the same day that 4 was published, and very soon afterward (same day, perhaps) I published a detailed followup. A few months later I added cross-links between all the articles. (See https://blog.plover.com/math/i-5.html )
* I'm sorry I didn't reply to your email at the time. I'm not sure that I received it. But I just checked, and I did get at least two messages on the topic that I didn't reply to, one from someone named Briggs and one from someone named Barlotti. If either of these is you, I apologize. I do try to answer blog-related email.
Neither is me, but it's no problem! You have often responded to my blog-related e-mails (my pessimistic Reddit comment that I didn't expect a reply was because it was probably my first time writing to you, and most bloggers without comments don't respond), and I have no proof that I sent this, so I might well have thought I did but left it sitting in draft, or something.
> * That article was #4 in a series. I did add a correction to article 3 the same day that 4 was published, and very soon afterward (same day, perhaps) I published a detailed followup. A few months later I added cross-links between all the articles. (See https://blog.plover.com/math/i-5.html )
I do see you mentioning in part 3 that there are many more additive-group automorphisms of ℝ, even continuous group automorphisms, than you had originally expected, and also mentioning (there and explicitly in part 5) that continuity is needed to say that there aren't even more. But I think that I probably did not make my point very clear, or else I am still misunderstanding.
I think (but can't say for sure—I refer to “the linked article”, because I missed that it was just a link to the math tag, so that I was just seeing whatever article was current at the time) that I was referring to https://blog.plover.com/math/i-4.html, which still seems to say:
> … the only automorphisms of the complex numbers are the identity function and the function a + bi → a - bi.
Here the context makes clear that "automorphism" means (at least) "field automorphism". It is this statement that I was disputing. You mention a streamlined argument that 1 is sent to 1 and −1 to −1, hence i to ±i; but that doesn't allow us to conclude—it only says that f(a + bi) equals f(a) ± f(b)i, and we are left to wonder what f is doing on ℝ. (The same issue crops up in the same way in part 3, which again seems more or less to conclude with the observation that f(i) = ±i.) Without continuity, we need not have that f restricts to the ‘standard’ embedding of ℝ in ℂ, and so have more than two field automorphisms of ℂ; but the construction of discontinuous automorphisms here is even scarier, using not just a Hamel basis of ℝ over ℚ but a transcendence basis (https://en.wikipedia.org/wiki/Transcendental_extension#Trans...) for ℂ over the algebraic closure of ℚ. A cute consequence that, to me, is proof enough that transcendence bases are a worthwhile concept: the field ℂ(X) of rational functions in one variable over ℂ admits a field embedding into ℂ; or, more precisely and to some people cuter, the algebraic closure of ℂ(X) is isomorphic to ℂ.
It is certainly true that, all possible fanciness aside, there are only two ℝ-algebra endomorphisms of ℂ, where the definition of an algebra map requires it to take multiplicative identity to multiplicative identity. In particular, we find that every ℝ-algebra endomorphism of ℂ is an automorphism, which sounds fancy when you say it with that many syllables.