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
1 #!/bin/sh
2
2 if [ "$BASH_VERSION" ]; then
3
3 substr() # $var_to_get $start $length $var_to_set
4
4 {
5
5 eval $4=\"\${$1:\$2${3:+:\$3}}\"
6
6 }
7
7 else
8
8 substr() # $var_to_get $start $length $var_to_set
9
9 {
10
10 eval local __tmp=\"\$1\" || return
11
11 local __start="${2:-0}" __len="$3"
12
12 local __tbuf __tbuf_len __trim __trimq
13
13
14
14 if [ ! "$__tmp" ]; then
15
15 eval $4=
16
16 return 0
17
17 fi
18
18 if [ "$__start" -lt 0 ] 2> /dev/null; then
19
19 __start=$(( ${#__tmp} + $__start ))
20
20 if [ $__start -lt 0 ]; then
21
21 eval $4=
22
22 return 1
23
23 fi
24
24 elif [ "$__start" -ge 0 ] 2> /dev/null; then
25
25 : valid number
26
26 else
27
27 __start=0
28
28 fi
29
29 if [ ! "$__len" ]; then
30
30 if [ $__start -eq 0 ]; then
31
31 eval $4=\"\$__tmp\"
32
32 return 0
33
33 fi
34
34 __len=$(( ${#__tmp} - $__start ))
35
35 elif [ "$__len" -lt 0 ] 2> /dev/null; then
36
36 __len=$(( ${#__tmp} - $__start + $__len ))
37
37 elif [ "$__len" -ge 1 ] 2> /dev/null; then
38
38 : valid number
39
39 else
40
40 eval $4=
41
41 return 1
42
42 fi
43
43
44
44 __trim=$__start
45
45 while [ $__trim -gt 0 ]; do
46
46 __tbuf="?"
47
47 __tbuf_len=1
48
48 while [ $__tbuf_len -lt $(( $__trim / $__tbuf_len )) ]
49
49 do
50
50 __tbuf="$__tbuf?"
51
51 __tbuf_len=$(( $__tbuf_len + 1 ))
52
52 done
53
53 __trimq=$(( $__trim / $__tbuf_len ))
54
54 __trim=$(( $__trim - $__tbuf_len * $__trimq ))
55
55 while [ $__trimq -gt 0 ]; do
56
56 __tmp="${__tmp#$__tbuf}"
57
57 __trimq=$(( $__trimq - 1 ))
58
58 done
59
59 done
60
60
61
61 local __tmp_len=${#__tmp}
62
62 local __mask __mask_len
63
63 __trim=$(( $__tmp_len - ${__len:-$__tmp_len} ))
64
64 while [ $__trim -gt 0 ]; do
65
65 __tbuf="?"
66
66 __tbuf_len=1
67
67 if [ $__trim -le $__len ]; then
68
68 while [ $__tbuf_len -lt $((
69
69 $__trim / $__tbuf_len
70
70 )) ]; do
71
71 __tbuf="$__tbuf?"
72
72 __tbuf_len=$(( $__tbuf_len + 1 ))
73
73 done
74
74 __trimq=$(( $__trim / $__tbuf_len ))
75
75 __trim=$(( $__trim - $__tbuf_len * $__trimq ))
76
76 while [ $__trimq -gt 0 ]; do
77
77 __tmp="${__tmp%$__tbuf}"
78
78 __trimq=$(( $__trimq - 1 ))
79
79 done
80
80 else
81
81 __mask="$__tmp"
82
82 while [ $__tbuf_len -lt $((
83
83 $__len / $__tbuf_len
84
84 )) ]; do
85
85 __tbuf="$__tbuf?"
86
86 __tbuf_len=$(( $__tbuf_len + 1 ))
87
87 done
88
88 __trimq=$(( $__len / $__tbuf_len ))
89
89 if [ $__len -ne $(( $__trimq * $__tbuf_len )) ]
90
90 then
91
91 __tbuf="$__tbuf?"
92
92 __tbuf_len=$(( $__tbuf_len + 1 ))
93
93 fi
94
94 __mask_len=$((
95
95 $__tmp_len - $__tbuf_len * $__trimq
96
96 ))
97
97 __trim=$(( $__tmp_len - $__mask_len - $__len ))
98
98 while [ $__trimq -gt 0 ]; do
99
99 __mask="${__mask#$__tbuf}"
100
100 __trimq=$(( $__trimq - 1 ))
101
101 done
102
102 __tmp="${__tmp%"$__mask"}"
103
103 fi
104
104 done
105
105
106
106 eval $4=\"\$__tmp\"
107
107 }
108
108 fi
109
109 substr "[email protected]"
110
110 eval echo \"$4=[\$4]\"
Copied!
Copy link