Created
August 19, 2022 16:50
-
-
Save alganet/70cf8b20a560adeae63ce867bfb2b2a9 to your computer and use it in GitHub Desktop.
Repeating strings and iterating over chars in portable shell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
set -euf | |
PATH= | |
IFS=' ' | |
# Shells like dash and posh don't have the substring parameter expansion | |
# commonly found in bash and zsh. | |
# | |
# To make iterating over each char fast, instead of breaking each | |
# char individually (which forces the shell to deal with the entire | |
# string twice, due to the nature of parameter substitution), we | |
# do consecutive breaks in half. | |
# | |
# The str_repeat function here is used to generate the specific | |
# pattern that splits an arbitrary string into its half. | |
# | |
# You should use this only if you want to support these kind | |
# of operations on these lofi shells. | |
# | |
! command -v emulate >/dev/null 2>&1 || emulate ksh >/dev/null 2>&1 | |
command -v local >/dev/null 2>&1 || alias local=typeset | |
_str_repeat () { | |
local str="$1" length_plus1=$(($2 + 1)) pow_of_two=1 next_pow= repeated= | |
# these cases (0-4) are not worth the cost of the while below | |
case $length_plus1 in | |
1) REPLY=""; return;; | |
2) REPLY="$1"; return;; | |
3) REPLY="$1$1"; return;; | |
4) REPLY="$1$1$1"; return;; | |
5) REPLY="$1$1$1$1"; return;; | |
esac | |
# doubles $repeated by powers of two | |
while next_pow=$((pow_of_two * 2)) | |
do | |
if test $next_pow -lt $length_plus1 | |
then | |
pow_of_two=$next_pow | |
str="$str$str" | |
else | |
length_plus1=$((length_plus1 - pow_of_two)) | |
if test $length_plus1 -lt 1 | |
then break | |
fi | |
repeated="$repeated$str" | |
str="$1" | |
pow_of_two=1 | |
fi | |
done | |
REPLY="$repeated" | |
} | |
_str_scan_chars () { | |
local result= left= right= result= any_char= | |
# changes the orientation of string cutting | |
if test "${1:-}" = 1 | |
then begin='#' end='%' | |
else begin='%' end='#' | |
fi | |
if test $# -gt 1 | |
then shift | |
fi | |
# recursively splits string in half, left side first | |
while test "$#" -gt 0 | |
do | |
if test -z "$1" | |
then shift && continue | |
fi | |
_str_repeat '?' $((${#1} / 2)) | |
any_char="$REPLY" | |
eval "right=\"\${1$begin$any_char}\"" | |
eval "left=\"\${1$end$right}\"" | |
shift | |
if test 1 = ${#left} | |
then | |
result="${result} ${left}" | |
left='' | |
fi | |
if test 1 = ${#right} | |
then | |
result="${result} ${right}" | |
right='' | |
fi | |
set -- "$left" "$right" "$@" | |
done | |
REPLY="$result" | |
} | |
_str_repeat '_' 1000; echo "${#REPLY}" | |
_str_scan_chars 1 "$REPLY"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment