@navi đ I love them. But you can also do this direct in shell without external commands if performance matters.
i do accept suggestions of a posix shell-native way to do that!
for context i'm working with a file path structure that goes `$subdir/%03d` and often need to hold the inner id in a variable
@navi "${1##+(0)} in ksh or bash with shopt -s extglob
in sh I generally would use a loop over forking thrice and spawning not so portable things, have more ideas tho
@navi ${x#0} should peel one zero. Repeating this N times even for decently large N is cheaper than a fork+exec. There's probably a better way too.
If you know it's 3 digits for example, $((1$x-1000)) works.
@dalias from the structured data itâll always be 3 digits, but user input not always
looking around, iâve just found out "${x#${x%%[!0]*}}" which by what i can tell should be portable
however, https://social.vlhl.dev/notice/B5pmpbtpl2X7xTZylM
@navi @dalias if itâs numeric, otherwise you probably want "${x#"${x%%[!0]*}"}". Iâve used this without nesting (i.e. with an intermediate variable) in the past but cannot tell you why⌠perhaps I needed the cut-off part in most cases anyway?
Anyway, here are more:
If youâre doing arithmetic on it anyway, i.e. know it fits the range (2³² in mksh, C long in lksh and other POSIX shells), and you know itâs a number, you can probably get the best performance on any kind of ksh-ish by doing:
if test -n "$KSH_VERSION"; then
x=$((10#$x))
else
# whatever you do for sh
fi
(Or, actually, define separate functions, if this is called often.)
So far, all the sh examples and the extglob one are unsafe if the value is literally 0. I assume you want to keep 0 as valid value.
The loop I mentioned and what dalias likely meant could be:
while :; do
case $x in
([1-9]*|0) break ;;
(*) x=${x#0} ;;
esac
done
Forking thrice and likely execing twice (printf is often not a builtin) and being unportable (printf only arrived recently in many OSes, and the addition of -E to sed is even more insanely recent) isnât nice. I can understand youâd not want ksh extglobs. That leaves us with⌠several variants of this, one somewhat tuned one could be (optionally remove the second line):
case $x in
([1-9]*) ;;
(*[1-9]*) x=${x#"${x%%[!0]*}"} ;;
(*) x=0 ;;
esac
This one has the assumption you know itâs a number. If not, skip the ksh part above (itâs also only safe for known numbers) and test for that first, which herein can be done elegantly: (again, optionally remove the now-third line)
case $x in
(*[!0-9]*) die NaN ;;
([1-9]*) ;;
(*[1-9]*) x=${x#${x%%[!0]*}} ;;
(*) x=0 ;;
esac
(Since we test for weird chars, can drop the double quotes again.)
A variation on this (safe for not-numbers but passing them through):
x=${x#"${x%%[!0]*}"}
x=${x:-0}
If youâre going to put that into a function, however, do make itâŚ
if test -n "$KSH_VERSION$BASH_VERSION"; then
test -z "$BASH_VERSION" || shopt -s extglob
unfuck_number() {
x=${x##+(0)}
x=${x:-0}
}
else
unfuck_number() {
x=${x#"${x%%[!0]*}"}
x=${x:-0}
}
fi
⌠instead, though; the extglob is that much faster internally. (And, yes, youâll normally want to just enable it for GNU bash; itâll be a nop if you stick to POSIX constructs anyway, and it allows you to provide ksh-bash-subset optimised functions where you want.)
In this case, youâll likely want something closer toâŚ
unfuck_number() {
local unfuck_number_tmp
eval "unfuck_number_tmp=\$$1"
unfuck_number_tmp=${unfuck_number_tmp#"${unfuck_number_tmp%%[!0]*}"}
eval "$1=\${unfuck_number_tmp:-0}"
}
⌠(takes an inout variable name) orâŚ
unfuck_number() {
local unfuck_number_tmp=${2#"${2%%[!0]*}"}
eval "$1=\${unfuck_number_tmp:-0}"
}
⌠(takes an out variable name and an in value). Equivalent for the extglob variant, of course, though note ksh93 has no local but addingâŚ
[[ $KSH_VERSION = Version* ]] && alias local=typeset
⌠in the ksh/bash block will do; this excludes ksh86 and ksh88, for which the tests are a bit more complex (see mircvs://contrib/code/Snippets/getshver if you want to add one for at least ksh88, which is an sh on many old systems).
IFS hacks likely arenât useful here.
Hmmh, already getting tired (loooooong day, bomb defusing, train chaos, choir, extreme train chaosâŚ) so this is all I can think of for now.
Sometimes I forget that you're the author of a shell, and then you post one of these and then I go đđś and I'm reminded again.
No I didn't read the full thing. Yes I did appreciate it đ
@navi @dalias
@navi @dalias @wouter tbh, just maintainer. The original author is Charles Forsyth of Inferno fame, who also responded kindly to my inquiry for a fallback licence for nĹn-PD jurisdictions. Very nice.
(That being said, yes, Iâve put over two decades worth of bugfixing, extending and redoing into it.)
Charles is the author in the same way that Linus is the author of Linux: he kickstarted it and wrote the initial batch of code, but he hasn't touched it in years, and most of the current code is yours.
You're the author.
@navi @dalias
- replies
- 0
- announces
- 0
- likes
- 1