# Why zsh Handles Some Shell-Scripting Edge Cases Better Than Bash

DevFeed: [Why zsh Handles Some Shell-Scripting Edge Cases Better Than Bash](<https://devfeed.tech/articles/s-bash-zsh-g-53020.md>)

Original publisher: [Read original article](<https://www.arp242.net/why-zsh.html>)

Author: Martin Tournoij

Published: 2021-10-19T23:00:00Z

Content type: opinion

Language: en

Sources: [arp242.net](<https://devfeed.tech/sources/arp242-net.md>)

Topics: [Zsh](<https://devfeed.tech/topics/zsh.md>), [Bash](<https://devfeed.tech/topics/bash.md>), [Shell](<https://devfeed.tech/topics/shell.md>), [Script](<https://devfeed.tech/topics/script.md>)

Tags: [bash](<https://devfeed.tech/tags/bash.md>), [scripting](<https://devfeed.tech/tags/scripting.md>), [shell](<https://devfeed.tech/tags/shell.md>), [shell-scripting](<https://devfeed.tech/tags/shell-scripting.md>), [unix](<https://devfeed.tech/tags/unix.md>), [zsh](<https://devfeed.tech/tags/zsh.md>)

## AI overview

The article argues that zsh handles several shell-scripting edge cases more conveniently than bash, including fractional arithmetic, NUL bytes, array assignment, binary content, and quoting. It also discusses POSIX compatibility and interactive loop syntax.

## Source excerpt

You would expect this to work, no? bash% echo $(( .1 + .2 )) bash: .1 + .2 : syntax error: operand expected (error token is ".1 + .2 ") Well, bash says no, but zsh just works: zsh% echo $(( .1 + .2 )) 0.30000000000000004 # Well, "works" insofar IEEE-754 works. There is simply no way you can do calculations with fractions in bash without relying on bc, dc, or some hacks. Compared to simply being able to use a + b it's ugly, slow, and difficult. There are other pretty frustrating omissions in bash; NUL bytes is another fun one: zsh% x=$(printf 'N\x00L'); printf $x | xxd -g1 -c3 00000000: 4e 00 4c N.L bash% x=$(printf 'N\x00L'); printf $x | xxd -g1 -c3 bash: warning: command substitution: ignored null byte in input 00000000: 4e 4c NL It looks like bash added a warning recently-ish (4.4-patch 2); this one bit me pretty hard a few years ago; back then it would just get silently discarded without warning; I guess a warning is an "improvement" of sorts (fixing it, however, would be an actual improvement1). NUL bytes aren't that uncommon, think of e.g. find -print0, xargs -0, etc. That all works grand, right up to the point you try to assign it to a variable. You can use NUL bytes for array assignments though, if you evoke the right incantation: bash% read -rad arr < <(find . -type f -print0) There are all sorts of edge-cases where you need to resort to read or readarray rather than being able to just assign in. In zsh it's: zsh% arr=(**/*(.)) zsh% IFS='\x00' arr=($(find . -type f -print0)) # If you must use find (rarely needed) zsh% arr=( "${(0)$(find . -type f -print0)}" ) That (.) is a "glob qualifier" to include only regular files - more on that later. Don't even think of doing something like: img=$(curl https://example.com/image.png) if [[ $cond ]]; then optpng <<<"$img" > out.png else cat <<<"$img" > out.png fi Of course you can refactor this to avoid the variable (and the example is a bit contrived), but it really ought to work. I once wrote a script to import emails