substr
Unlike Bourne Again Shell (bash), POSIX shell does not have a native substring function. Below is an implementation written in native POSIX shell that is:
  • Faster than piping to awk, perl, python, ruby, or other langauge
  • Compatible with bash and all other POSIX-compliant shells
  • Optimized to use ${foo:start[:length]} when bash is detected
1 #!/bin/sh
2 if [ "$BASH_VERSION" ]; then
3 substr() # $var_to_get $start $length $var_to_set
4 {
5 eval $4=\"\${$1:\$2${3:+:\$3}}\"
6 }
7 else
8 substr() # $var_to_get $start $length $var_to_set
9 {
10 eval local __tmp=\"\$1\" || return
11 local __start="${2:-0}" __len="$3"
12 local __tbuf __tbuf_len __trim __trimq
13
14 if [ ! "$__tmp" ]; then
15 eval $4=
16 return 0
17 fi
18 if [ "$__start" -lt 0 ] 2> /dev/null; then
19 __start=$(( ${#__tmp} + $__start ))
20 if [ $__start -lt 0 ]; then
21 eval $4=
22 return 1
23 fi
24 elif [ "$__start" -ge 0 ] 2> /dev/null; then
25 : valid number
26 else
27 __start=0
28 fi
29 if [ ! "$__len" ]; then
30 if [ $__start -eq 0 ]; then
31 eval $4=\"\$__tmp\"
32 return 0
33 fi
34 __len=$(( ${#__tmp} - $__start ))
35 elif [ "$__len" -lt 0 ] 2> /dev/null; then
36 __len=$(( ${#__tmp} - $__start + $__len ))
37 elif [ "$__len" -ge 1 ] 2> /dev/null; then
38 : valid number
39 else
40 eval $4=
41 return 1
42 fi
43
44 __trim=$__start
45 while [ $__trim -gt 0 ]; do
46 __tbuf="?"
47 __tbuf_len=1
48 while [ $__tbuf_len -lt $(( $__trim / $__tbuf_len )) ]
49 do
50 __tbuf="$__tbuf?"
51 __tbuf_len=$(( $__tbuf_len + 1 ))
52 done
53 __trimq=$(( $__trim / $__tbuf_len ))
54 __trim=$(( $__trim - $__tbuf_len * $__trimq ))
55 while [ $__trimq -gt 0 ]; do
56 __tmp="${__tmp#$__tbuf}"
57 __trimq=$(( $__trimq - 1 ))
58 done
59 done
60
61 local __tmp_len=${#__tmp}
62 local __mask __mask_len
63 __trim=$(( $__tmp_len - ${__len:-$__tmp_len} ))
64 while [ $__trim -gt 0 ]; do
65 __tbuf="?"
66 __tbuf_len=1
67 if [ $__trim -le $__len ]; then
68 while [ $__tbuf_len -lt $((
69 $__trim / $__tbuf_len
70 )) ]; do
71 __tbuf="$__tbuf?"
72 __tbuf_len=$(( $__tbuf_len + 1 ))
73 done
74 __trimq=$(( $__trim / $__tbuf_len ))
75 __trim=$(( $__trim - $__tbuf_len * $__trimq ))
76 while [ $__trimq -gt 0 ]; do
77 __tmp="${__tmp%$__tbuf}"
78 __trimq=$(( $__trimq - 1 ))
79 done
80 else
81 __mask="$__tmp"
82 while [ $__tbuf_len -lt $((
83 $__len / $__tbuf_len
84 )) ]; do
85 __tbuf="$__tbuf?"
86 __tbuf_len=$(( $__tbuf_len + 1 ))
87 done
88 __trimq=$(( $__len / $__tbuf_len ))
89 if [ $__len -ne $(( $__trimq * $__tbuf_len )) ]
90 then
91 __tbuf="$__tbuf?"
92 __tbuf_len=$(( $__tbuf_len + 1 ))
93 fi
94 __mask_len=$((
95 $__tmp_len - $__tbuf_len * $__trimq
96 ))
97 __trim=$(( $__tmp_len - $__mask_len - $__len ))
98 while [ $__trimq -gt 0 ]; do
99 __mask="${__mask#$__tbuf}"
100 __trimq=$(( $__trimq - 1 ))
101 done
102 __tmp="${__tmp%"$__mask"}"
103 fi
104 done
105
106 eval $4=\"\$__tmp\"
107 }
108 fi
109 substr "[email protected]"
110 eval echo \"$4=[\$4]\"
Copy link