Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 28 additions & 36 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,6 @@ on:
branches: [ master, for-master ]

jobs:
lint-shell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "install tools"
run: sudo apt-get -y -qq install shellcheck
- name: "shellcheck"
run: make verify

check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "install tools"
run: sudo apt-get -y -qq install scdoc
- name: "make docs"
run: make man

check-commits:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -52,32 +34,42 @@ jobs:
exit 1
fi

check-dash:
lint-shell:
runs-on: ubuntu-latest
needs: [ lint-shell ]
steps:
- uses: actions/checkout@v4
- name: "install tools"
run: sudo apt-get -y -qq install dash bash mksh
- name: "unittests"
run: make check CHECK_SHELL=/bin/dash V=1
- run: sudo apt-get -y -qq install shellcheck
- run: make verify

check-bash:
check-docs:
runs-on: ubuntu-latest
needs: [ lint-shell ]
steps:
- uses: actions/checkout@v4
- name: "install tools"
run: sudo apt-get -y -qq install dash bash mksh
- name: "unittests"
run: make check CHECK_SHELL=/bin/bash V=1
- run: sudo apt-get -y -qq install scdoc
- run: make man

check-mksh:
check-shells:
runs-on: ubuntu-latest
needs: [ lint-shell ]
needs:
- lint-shell
strategy:
fail-fast: false
matrix:
include:
- name: dash
pkgs: dash

- name: bash
pkgs: bash

- name: mksh
pkgs: mksh

- name: lksh
pkgs: mksh

name: "check with ${{ matrix.name }}"
steps:
- uses: actions/checkout@v4
- name: "install tools"
run: sudo apt-get -y -qq install dash bash mksh
- name: "unittests"
run: make check CHECK_SHELL=/bin/mksh V=1
- run: sudo apt-get -y -qq install ${{ matrix.pkgs }}
- run: make check V=1 CHECK_SHELL=/bin/${{ matrix.name }}
184 changes: 184 additions & 0 deletions Documentation/int64_max.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Maximum Int64 Number

Many shell implementations support 64-bit numbers, but there is a problem with
parsing their maximum values. Some shell implementations handle this
differently.

It is notable that the behavior differs between dash and ash from busybox.

## Agenda

* zsh - zsh (5.9) - UNIX Shell similar to the Korn shell (https://www.zsh.org/)
* dash - dash (0.5.12) - Debian Almquist Shell (https://git.kernel.org/pub/scm/utils/dash/dash.git)
* bash - bash (5.3_p3) - GNU Bourne-Again Shell (https://git.savannah.gnu.org/cgit/bash.git)
* ksh - loksh (7.7) - Linux port of OpenBSD's ksh (https://github.com/dimkr/loksh/)
* lksh - mksh (59c) - MirBSD Korn Shell (https://mbsd.evolvis.org/mksh.htm)
* ash - busybox (1.36.1) - Tiny utilities for small and embedded systems (https://busybox.net/)

## Value Calculation

```sh
$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %d\n" "$0" $(( 1 << 62 ))'; done
OUT: zsh: 4611686018427387904
OUT: dash: 4611686018427387904
OUT: bash: 4611686018427387904
OUT: ksh: 4611686018427387904
OUT: lksh: 4611686018427387904
OUT: ash: 4611686018427387904

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %s\n" "$0" $(( 1 << 63 ))'; done
OUT: zsh: -9223372036854775808
OUT: dash: -9223372036854775808
OUT: bash: -9223372036854775808
OUT: ksh: -9223372036854775808
OUT: lksh: -9223372036854775808
OUT: ash: -9223372036854775808

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %x\n" "$0" $(( 1 << 63 ))'; done
zsh:1: number truncated after 18 digits: 9223372036854775808
OUT: zsh: f333333333333334
OUT: dash: 8000000000000000
OUT: bash: 8000000000000000
OUT: ksh: 8000000000000000
OUT: lksh: 8000000000000000
OUT: ash: 8000000000000000
```

```sh
$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %s\n" "$0" $(( 0x7fffffffffffffff + 1 ))'; done
OUT: zsh: -9223372036854775808
OUT: dash: -9223372036854775808
OUT: bash: -9223372036854775808
OUT: ksh: -9223372036854775808
OUT: lksh: -9223372036854775808
OUT: ash: -9223372036854775808

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %x\n" "$0" $(( 0x7fffffffffffffff + 1 ))'; done
zsh:1: number truncated after 18 digits: 9223372036854775808
OUT: zsh: f333333333333334
OUT: dash: 8000000000000000
OUT: bash: 8000000000000000
OUT: ksh: 8000000000000000
OUT: lksh: 8000000000000000
OUT: ash: 8000000000000000

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %x\n" "$0" $(( 0x7fffffffffffffff + 2 ))'; done
OUT: zsh: 8000000000000001
OUT: dash: 8000000000000001
OUT: bash: 8000000000000001
OUT: ksh: 8000000000000001
OUT: lksh: 8000000000000001
OUT: ash: 8000000000000001

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %x\n" "$0" $(( 0x7fffffffffffffff + 3 ))'; done
OUT: zsh: 8000000000000002
OUT: dash: 8000000000000002
OUT: bash: 8000000000000002
OUT: ksh: 8000000000000002
OUT: lksh: 8000000000000002
OUT: ash: 8000000000000002

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %x\n" "$0" $(( 0x7fffffffffffffff + 0x7fffffffffffffff + 1 ))'; done
OUT: zsh: ffffffffffffffff
OUT: dash: ffffffffffffffff
OUT: bash: ffffffffffffffff
OUT: ksh: ffffffffffffffff
OUT: lksh: ffffffffffffffff
OUT: ash: ffffffffffffffff
```

There are anomalies in parsing large numbers:

```sh
$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %x\n" "$0" $(( 0xffffffffffffffff ))'; done
zsh:1: number truncated after 15 digits: ffffffffffffffff
OUT: zsh: fffffffffffffff
OUT: dash: 7fffffffffffffff
OUT: bash: ffffffffffffffff
OUT: ksh: ffffffffffffffff
OUT: lksh: ffffffffffffffff
OUT: ash: ffffffffffffffff

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %s\n" "$0" $(( 0x8000000000000000 ))'; done
zsh:1: number truncated after 15 digits: 8000000000000000
OUT: zsh: 576460752303423488
OUT: dash: 9223372036854775807
OUT: bash: -9223372036854775808
OUT: ksh: -9223372036854775808
OUT: lksh: -9223372036854775808
OUT: ash: -9223372036854775808

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'printf "OUT: %4s: %d\n" "$0" $(( -9223372036854775808 ))'; done
zsh:1: number truncated after 18 digits: 9223372036854775808
OUT: zsh: -922337203685477580
OUT: dash: -9223372036854775807
OUT: bash: -9223372036854775808
OUT: ksh: -9223372036854775808
OUT: lksh: -9223372036854775808
OUT: ash: -9223372036854775808
```

## Variables

And one more thing. If you put some value in the variable, then in further
calculations, parsing will happen again, which might have an extra effect.

```sh
$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'v=$(( 1 << 62 )); printf "OUT: %4s: %x\n" $0 $(( $v ))'; done
OUT: zsh: 4000000000000000
OUT: dash: 4000000000000000
OUT: bash: 4000000000000000
OUT: ksh: 4000000000000000
OUT: lksh: 4000000000000000
OUT: ash: 4000000000000000

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'v=0x7fffffffffffffff; printf "OUT: %4s: %x\n" $0 $(( $v ))'; done
OUT: zsh: 7fffffffffffffff
OUT: dash: 7fffffffffffffff
OUT: bash: 7fffffffffffffff
OUT: ksh: 7fffffffffffffff
OUT: lksh: 7fffffffffffffff
OUT: ash: 7fffffffffffffff

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'v=$(( 1 << 63 )); printf "OUT: %4s: %x\n" $0 $(( $v ))'; done
zsh:1: number truncated after 18 digits: 9223372036854775808
OUT: zsh: f333333333333334
OUT: dash: 8000000000000001
OUT: bash: 8000000000000000
OUT: ksh: 8000000000000000
OUT: lksh: 8000000000000000
OUT: ash: 8000000000000000

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'v=0x8000000000000000; printf "OUT: %4s: %x\n" $0 $(( $v ))'; done
zsh:1: number truncated after 15 digits: 8000000000000000
OUT: zsh: 800000000000000
OUT: dash: 7fffffffffffffff
OUT: bash: 8000000000000000
OUT: ksh: 8000000000000000
OUT: lksh: 8000000000000000
OUT: ash: 8000000000000000

$ for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'v=$(( 0x7fffffffffffffff + 1 )); printf "OUT: %4s: %x\n" $0 $(( $v ))'; done
zsh:1: number truncated after 18 digits: 9223372036854775808
OUT: zsh: f333333333333334
OUT: dash: 8000000000000001
OUT: bash: 8000000000000000
OUT: ksh: 8000000000000000
OUT: lksh: 8000000000000000
OUT: ash: 8000000000000000

for sh in zsh dash bash ksh lksh "busybox ash"; do $sh -c 'v="(0x7fffffffffffffff + 1)"; printf "OUT: %4s: %x\n" $0 $(( $v ))'; done
zsh:1: number truncated after 18 digits: 9223372036854775808
OUT: zsh: f333333333333334
OUT: dash: 8000000000000000
OUT: bash: 8000000000000000
OUT: ksh: 8000000000000000
OUT: lksh: 8000000000000000
OUT: ash: 8000000000000000
```

It seems that if one wants to have the value `1 << 63` in a variable, it is more
correct to keep it as "(0x7fffffffffffffff + 1)" string.

--
Rgrds, legion
44 changes: 26 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ capability_TARGETS = shell-regexp
bin_TARGETS = $(filter-out shell-lib,$(wildcard shell-*))
data_TARGETS = COPYING

mddocs_TARGETS = $(wildcard docs/shell-*.md)
docs_TARGETS = docs/libshell.md $(mddocs_TARGETS)
man_TARGETS = $(docs_TARGETS:.md=.3)
mandocs_TARGETS = $(wildcard mans/shell-*.scd)
docs_TARGETS = mans/libshell.scd $(mandocs_TARGETS)
man_TARGETS = $(docs_TARGETS:.scd=.3)

.PHONY: $(SUBDIRS)

Expand All @@ -40,7 +40,7 @@ shell-lib: ${bin_TARGETS}
shell-regexp: shell-quote
ln -s -- $^ $@

%.3: %.md
%.3: %.scd
@[ -z "$(SCDOC)" ] || $(SCDOC) < $< > $@

man: ${man_TARGETS}
Expand Down Expand Up @@ -81,20 +81,27 @@ $(PROJECT)-$(VERSION).tar.sign: $(PROJECT)-$(VERSION).tar.xz
tar: $(PROJECT)-$(VERSION).tar.xz
release: $(PROJECT)-$(VERSION).tar.sign

check:
@cd tests; \
for sh in $${CHECK_SHELL:-/bin/sh /bin/dash /bin/bash /bin/bash3 /bin/bash4 /bin/mksh /bin/yash /bin/pdksh}; do \
[ -x "$$sh" ] || continue; \
export TEST_SHELL="$$sh"; \
echo "Running tests with $$sh"; \
if ! "$$sh" -efu ./runtests; then \
echo "Tests failed with $$sh"; \
CHECK_SHELL =
CHECK_SHELL += /bin/sh # POSIX Shell
CHECK_SHELL += /bin/dash # Debian Almquist Shell (https://git.kernel.org/pub/scm/utils/dash/dash.git)
CHECK_SHELL += /bin/bash /bin/bash3 /bin/bash4 # GNU Bourne-Again Shell (https://git.savannah.gnu.org/cgit/bash.git)
CHECK_SHELL += /bin/mksh /bin/lksh # MirBSD Korn Shell (https://mbsd.evolvis.org/mksh.htm)

check-tests:
@cd tests; rc=0; \
for TEST_SHELL in $(wildcard $(CHECK_SHELL)); do export TEST_SHELL; \
echo "Running tests with $$TEST_SHELL"; \
if ! $$TEST_SHELL -efu ./runtests; then \
echo >&2 "ERROR: tests failed."; \
echo; \
exit 1; \
rc=1; \
fi; \
done
@sed -n -e 's/^## \([^[:space:]]\+\)$$/\1/p' ${mddocs_TARGETS} |sort -uo "$(CURDIR)/.shell-funcs-documented"
@sed -n -e 's/^\([A-Za-z][A-Za-z0-9_]\+\)().*/\1/p' ${bin_TARGETS} |sort -uo "$(CURDIR)/.shell-funcs"
done; \
exit $$rc;

check-documented:
@sed -n -e 's/^## \([^[:space:]]\+\)$$/\1/p' ${mandocs_TARGETS} |sort -uo "$(CURDIR)/.shell-funcs-documented"
@sed -n -e 's/^\([A-Za-z][A-Za-z0-9_]\+\)().*/\1/p' ${bin_TARGETS} |sort -uo "$(CURDIR)/.shell-funcs"
@comm -13 \
"$(CURDIR)/.shell-funcs-documented" \
"$(CURDIR)/.shell-funcs" \
Expand All @@ -103,18 +110,19 @@ check:
if [ "$$(wc -l < "$(CURDIR)/.shell-funcs-not-documented")" != "0" ]; then \
echo >&2 "ERROR: some functions are not documented:"; \
cat "$(CURDIR)/.shell-funcs-not-documented"; \
echo; \
rc=1; \
else \
echo "All functions are documented."; \
echo; \
fi; \
echo; \
rm -f -- \
"$(CURDIR)/.shell-funcs-documented" \
"$(CURDIR)/.shell-funcs-not-documented" \
"$(CURDIR)/.shell-funcs"; \
exit $$rc;

check: check-tests check-documented

NULL =
SPACE = $(NULL) $(NULL)
COMMA = ,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Install dependencies:

## Documentation

See [docs](docs/libshell.md)
See [docs](mans/libshell.scd)

## LICENSE

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading