Here implementation of similar function but for which index position is specified from the start of the string.
How looks implementation of general function for which index position can be specified from the end of the string?
Example with input parameters and expected result for the required function / procedure:
string: "2278"
string_to_insert: "."
index_position: 3
# After inserting "." to 3-rd index position from the end of "string" result should be:
expected_result="2.278"
Related question:
This question is part of the project related to my another question: How to retrieve text from the current line at specified cursor position before and after up to specified boundary characters?
bash
parameter expansion:string="2278"
string_to_insert="."
index_position=3
result=${string::-index_position}$string_to_insert${string: -index_position}
echo $result
2.278
... Or even:
if (( index_position <= ${#string} )); then
result=${string::-index_position}$string_to_insert${string: -index_position}
echo $result
else
echo "Error string '$string' too small"
fi
More informations about this in the bash's man page, in Parameter Expansion section search for "Substring Expansion":
man -Pless\ '+/^\ *Parameter\ *Expansion\|Substring\ *Expansion' bash
If you want this as a function, regarding the first link shown in you request which stand for same request, but from start of string instead of end, I would suggest to simply use same convention than used in bash, where using negative position mean from end of string, so we just have to write one function for both operations:
insertAt() {
if [[ $1 == -v ]]; then
local -n __iAt_result=$2
local __iAt_setVar=true
shift 2
else
local __iAt_result __iAt_setVar=false
fi
(( $# != 3 )) &&
printf 'Usage: %s [-v <varname>] <source string> <string to insert> <[-]position>\n' \
"${FUNCNAME[0]}" >&2 &&
return 1
case ${3#-} in *[^0-9]* )
echo 'Error: 3rd argument must be an integer!' >&2
return 1 ;;
esac
local __iAt_string=$1 __iAt_toInsert=$2 __iAt_position=$3
(( ${__iAt_position#-} > ${#__iAt_string} )) &&
echo 'Error: position out of string length' >&2 &&
return 1
printf -v __iAt_result '%s%s%s' "${__iAt_string::__iAt_position}" \
"$__iAt_toInsert" "${__iAt_string: __iAt_position}"
$__iAt_setVar || echo "$__iAt_result"
}
insertAt 314159265 . 1
3.14159265
string="Hell!"
insertAt -v string "$string" 'o world' -1
echo "$string"
Hello world!
insertAt 314159265 1 .
Error: 3rd argument must be an integer!
Then if you try:
for i in {-5..5};do
unset result
insertAt -v result 2283 . $i
[[ -v result ]] && printf '%5d: %s\n' $i "$result"
done
You will see:
Error: position out of string length
-4: .2283
-3: 2.283
-2: 22.83
-1: 228.3
0: .2283
1: 2.283
2: 22.83
3: 228.3
4: 2283.
Error: position out of string length
POSIX shell
:In POSIX sh, string indexing is undefined.
So for doing this we have to find another way.
POSIX shell
way using sed
:Using only one fork to sed
:
insertAt() {
if [ "$1" = "-v" ]; then
__iAt_setVar=true __iAt_result=$2
shift 2
else
__iAt_setVar=false __iAt_result=__iAt_result
fi
[ $# -ne 3 ] && echo >&2 \
'Usage: insertAt [-v <varname>] <source string> <string to insert> <[-]position>' &&
return 1
case ${3#-} in
*[!0-9]*|'' )
echo 'Error: 3rd argument must be an integer!' >&2
return 1 ;;
esac
[ "${3#-}" -gt ${#1} ] &&
echo 'Error: position out of string length' >&2 &&
return 1
case $3 in
-* )
read -r "${__iAt_result?}" <<EOInLine__IAt_Result
$(echo "$1" | sed -e "s/.\{${3#-}\}$/${2}&/")
EOInLine__IAt_Result
;;
* )
read -r "${__iAt_result?}" <<EOInLine__IAt_Result
$(echo "$1" | sed -e "s/^.\{$3\}/&${2}/")
EOInLine__IAt_Result
;;
esac
if ! $__iAt_setVar; then echo "$__iAt_result"; fi
}
Will work exactly same:
insertAt 314159265 . 1
3.14159265
string="Hell!"
insertAt -v string "$string" 'o world' -1
echo "$string"
Hello world!
insertAt 314159265 1 .
Error: 3rd argument must be an integer!
for i in $(seq -- -5 5); do
unset result
insertAt -v result 2283 . "$i"
[ -n "$result" ] && printf '%5d: %s\n' "$i" "$result"
done
Error: position out of string length
-4: .2283
-3: 2.283
-2: 22.83
-1: 228.3
0: .2283
1: 2.283
2: 22.83
3: 228.3
4: 2283.
Error: position out of string length
shell
without forkBut with loops! Suitable for small strings as cited in you sample.
Under posix shell, we could use either Remove matching prefix pattern or Remove matching suffix pattern, depending on which operation (from start or from end) are to be done.
A little condensed for quick display here, on SO:
insertAt() {
if [ "$1" = "-v" ]; then __iAt_setVar=true __iAt_result=$2; shift 2
else __iAt_setVar=false __iAt_result=__iAt_result; fi
[ $# -ne 3 ] && echo >&2 \
'Usage: insertAt [-v <varname>] <source string> <string to insert> <[-]position>' &&
return 1
case ${3#-} in *[!0-9]*|'' )
echo 'Error: 3rd argument must be an integer!' >&2
return 1 ;; esac
[ "${3#-}" -gt ${#1} ] &&
echo 'Error: position out of string length' >&2 &&
return 1
__iAt_var1="" __iAt_var2="" __iAt_cnt=$(( ${#1} - ${3#-} ))
while [ $__iAt_cnt -gt 0 ]; do
__iAt_var1="?$__iAt_var1" __iAt_cnt=$((__iAt_cnt-1))
done
__iAt_cnt=$(( ${3#-} ))
while [ $__iAt_cnt -gt 0 ]; do
__iAt_var2="?$__iAt_var2" __iAt_cnt=$((__iAt_cnt-1))
done
case $3 in -* )
read -r "${__iAt_result?}" <<EOInLine__IAt_Result
${1%$__iAt_var2}$2${1#$__iAt_var1}
EOInLine__IAt_Result
;; * )
read -r "${__iAt_result?}" <<EOInLine__IAt_Result
${1%$__iAt_var1}$2${1#$__iAt_var2}
EOInLine__IAt_Result
;; esac
if ! $__iAt_setVar; then echo "$__iAt_result"; fi
}
Then again:
insertAt 314159265 . 1
3.14159265
string="Hell!"
insertAt -v string "$string" 'o world' -1
echo "$string"
Hello world!
insertAt 314159265 1 .
Error: 3rd argument must be an integer!
for i in $(seq -- -5 5); do
unset result
insertAt -v result 2283 . "$i"
[ -n "$result" ] && printf '%5d: %s\n' "$i" "$result"
done
Error: position out of string length
-4: .2283
-3: 2.283
-2: 22.83
-1: 228.3
0: .2283
1: 2.283
2: 22.83
3: 228.3
4: 2283.
Error: position out of string length