# The slight differences between Unix tools across BSD (incl. macOS), GNU/Linux, and Busybox
This is a list of discrepencies I came across while trying to while cross-system
compatible makefiles and Bash scripts.
## sed
`-i` (in-place editing) can work without an argument with GNU sed, but won't work
without an argument on BSD sed and on Busybox.
On BSD sed and Busybox sed, you have to give an argument that corresponds to the suffix
added to the file name when the back up file is created. The following works on BSD, GNU,
and Busybox sed:
```sh
sed -i.bak 's|foo|bar|' file
rm -f file.bak
```
Alternatively, you can use `perl`:
```bash
perl -pie 's/foo/bar/g'
```
`-E` doesn't work with BSD sed but works on Busybox and GNU sed. Because of this,
`|`, `+`, `\s`, `?`, and `\t` don't work, as explained in the article
[BSD sed vs. GNU sed](https://riptutorial.com/sed/topic/9436). For example,
```sh
echo somefoootherbartext | sed -E "s/(foo|bar)//g"
```
produces:
```sh
# GNU sed
someothertext
# BSD sed
somefoootherbartext
# Busybox sed
someothertext
```
Instead you can use:
```sh
sed -e "s/foo//g" -e "s/bar//g"
```
## awk
`-i` (in-place editing) only works [^1] with recent versions of GNU awk (4.1.0 and above). On BSD, the only solution is to use a temporary file:
```sh
awk '{print $0}' file > tmp && mv tmp file
```
[^1]: https://stackoverflow.com/questions/16529716/save-modifications-in-place-with-awk
## xargs
`-I'{}'`: BSD xargs only replaces the 5 first arguments. For example,
```
echo OK | xargs -I'{}' echo arg1-'{}' arg2-'{}' arg3-'{}' arg4-'{}' arg5-'{}' arg6-'{}'
```
produces:
```sh
# GNU xargs
arg1-OK arg2-OK arg3-OK arg4-OK arg5-OK arg6-OK
# BSD xargs
arg1-OK arg2-OK arg3-OK arg4-OK arg5-OK arg6
# Busybox xargs
arg1-OK arg2-OK arg3-OK arg4-OK arg5-OK arg6-OK
```
The flag `--max-procs` is only available on GNU xargs and BSD xargs but
not on Busybox xargs. Instead, you can use `-P`.
## date
`-v` (adjust value) only works on BSD date. The equivalent on GNU date is `-d` but with a slightly different way of giving the duration. On busybox, this flag doesn't exist.
| BSD | GNU | Busybox |
|--|--|--|
| `date -v+12H` | `date -d+12hours` | not available |
| `Thu Oct 13 20:43:03 CEST 2022` | `Thu Oct 13 08:44:52 PM CEST 2022` | n/a |
## find
> **NOTE:** On macOS find and GNU find, `-d` stands for "depth-first", to not be confused with `-maxdepth` which is compatible with macOS and GNU. Using `-d` instead of `-maxdepth` will result in the following message:
>
> ```console
> $ find . -depth 1
> find: paths must precede expression: `1'
> ```
On macOS, `-printf "%P\n"` won't work. On GNU find, `%P` displays the relative path instead of
the absolute path whe `find` is being run with a different folder than the current folder.
```console
$ find . -maxdepth 1 -type f -printf "%P\n"
find: -printf: unknown primary or operator
```
**Workaround:**
It's tricky, and it all depends on what pattern you are using in `-printf`. Regarding `%P`, you can replace it with:
```bash
find . -maxdepth 1 -type f | xargs basename
```
Read more about how to substitute `-printf` for macOS find: https://stackoverflow.com/questions/752818/find-lacks-the-option-printf-now-what.
## `uuidgen` (macOS) vs. `uuidgen` (GNU)
On Ubuntu, `uuidgen` belongs to the `uuid-runtime` package. The UUIDs are printed in lower case, e.g.
```
74b492c6-9568-49ca-a395-58ab93bd4b9b
```
On macOS, the UUIDs are printed in upper case:
```
EEF45689-BBE5-4FB6-9E80-41B78F6578E2
```
## Dash vs. Bourne shell (sh)