diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-22 03:12:29 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-22 03:12:29 +0200 |
commit | 1e811b12317d0eab4e78d848caa640cca497a0a7 (patch) | |
tree | ef60262af7944dfe557542bec9e1fb8bea367b60 | |
parent | ddc62f64baad8abdeb587b13afe8d47fb347981c (diff) | |
download | busybox-w32-1e811b12317d0eab4e78d848caa640cca497a0a7.tar.gz busybox-w32-1e811b12317d0eab4e78d848caa640cca497a0a7.tar.bz2 busybox-w32-1e811b12317d0eab4e78d848caa640cca497a0a7.zip |
hush: support ${var:EXPR:EXPR}!
function old new delta
handle_dollar 574 681 +107
expand_and_evaluate_arith - 77 +77
expand_vars_to_list 2302 2374 +72
add_till_closing_bracket 359 368 +9
builtin_exit 48 47 -1
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/1 up/down: 265/-1) Total: 264 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 130 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_bash_substring.right | 10 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_bash_substring.tests | 98 |
3 files changed, 152 insertions, 86 deletions
diff --git a/shell/hush.c b/shell/hush.c index 0f0151a21..41d5fcab2 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -171,6 +171,7 @@ | |||
171 | #define debug_printf_env(...) do {} while (0) | 171 | #define debug_printf_env(...) do {} while (0) |
172 | #define debug_printf_jobs(...) do {} while (0) | 172 | #define debug_printf_jobs(...) do {} while (0) |
173 | #define debug_printf_expand(...) do {} while (0) | 173 | #define debug_printf_expand(...) do {} while (0) |
174 | #define debug_printf_varexp(...) do {} while (0) | ||
174 | #define debug_printf_glob(...) do {} while (0) | 175 | #define debug_printf_glob(...) do {} while (0) |
175 | #define debug_printf_list(...) do {} while (0) | 176 | #define debug_printf_list(...) do {} while (0) |
176 | #define debug_printf_subst(...) do {} while (0) | 177 | #define debug_printf_subst(...) do {} while (0) |
@@ -743,6 +744,10 @@ static const struct built_in_command bltins2[] = { | |||
743 | # define DEBUG_EXPAND 0 | 744 | # define DEBUG_EXPAND 0 |
744 | #endif | 745 | #endif |
745 | 746 | ||
747 | #ifndef debug_printf_varexp | ||
748 | # define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__)) | ||
749 | #endif | ||
750 | |||
746 | #ifndef debug_printf_glob | 751 | #ifndef debug_printf_glob |
747 | # define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) | 752 | # define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) |
748 | # define DEBUG_GLOB 1 | 753 | # define DEBUG_GLOB 1 |
@@ -1817,11 +1822,11 @@ static void o_addblock(o_string *o, const char *str, int len) | |||
1817 | o->data[o->length] = '\0'; | 1822 | o->data[o->length] = '\0'; |
1818 | } | 1823 | } |
1819 | 1824 | ||
1820 | #if !BB_MMU | ||
1821 | static void o_addstr(o_string *o, const char *str) | 1825 | static void o_addstr(o_string *o, const char *str) |
1822 | { | 1826 | { |
1823 | o_addblock(o, str, strlen(str)); | 1827 | o_addblock(o, str, strlen(str)); |
1824 | } | 1828 | } |
1829 | #if !BB_MMU | ||
1825 | static void nommu_addchr(o_string *o, int ch) | 1830 | static void nommu_addchr(o_string *o, int ch) |
1826 | { | 1831 | { |
1827 | if (o) | 1832 | if (o) |
@@ -2597,12 +2602,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2597 | } else { | 2602 | } else { |
2598 | /* maybe handle parameter expansion */ | 2603 | /* maybe handle parameter expansion */ |
2599 | exp_saveptr = var + strcspn(var, "%#:-=+?"); | 2604 | exp_saveptr = var + strcspn(var, "%#:-=+?"); |
2600 | exp_save = *exp_saveptr; | 2605 | exp_op = exp_save = *exp_saveptr; |
2601 | if (exp_save) { | 2606 | if (exp_op) { |
2602 | exp_word = exp_saveptr; | 2607 | exp_word = exp_saveptr + 1; |
2603 | if (exp_save == ':') | 2608 | if (exp_op == ':') { |
2604 | exp_word++; | 2609 | exp_op = *exp_word++; |
2605 | exp_op = *exp_word++; | 2610 | if (ENABLE_HUSH_BASH_COMPAT |
2611 | && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op)) | ||
2612 | ) { | ||
2613 | /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ | ||
2614 | exp_op = ':'; | ||
2615 | exp_word--; | ||
2616 | } | ||
2617 | } | ||
2606 | *exp_saveptr = '\0'; | 2618 | *exp_saveptr = '\0'; |
2607 | } | 2619 | } |
2608 | } | 2620 | } |
@@ -2656,39 +2668,42 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2656 | *loc = '\0'; | 2668 | *loc = '\0'; |
2657 | } | 2669 | } |
2658 | } | 2670 | } |
2659 | } else if (!strchr("%#:-=+?"+3, exp_op)) { | 2671 | } else if (exp_op == ':') { |
2660 | #if ENABLE_HUSH_BASH_COMPAT | 2672 | #if ENABLE_HUSH_BASH_COMPAT |
2661 | /* exp_op is ':' and next char isn't a subst operator. | 2673 | /* It's ${var:N[:M]} bashism. |
2662 | * Assuming it's ${var:[N][:M]} bashism. | 2674 | * Note that in encoded form it has TWO parts: |
2663 | * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc | 2675 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> |
2664 | */ | 2676 | */ |
2665 | char *end; | 2677 | arith_t beg, len; |
2666 | unsigned len = INT_MAX; | 2678 | int errcode = 0; |
2667 | unsigned beg = 0; | 2679 | |
2668 | end = --exp_word; | 2680 | beg = expand_and_evaluate_arith(exp_word, &errcode); |
2669 | if (*exp_word != ':') /* not ${var::...} */ | 2681 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); |
2670 | beg = bb_strtou(exp_word, &end, 0); | 2682 | *p++ = SPECIAL_VAR_SYMBOL; |
2671 | //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); | 2683 | exp_word = p; |
2672 | if (*end == ':') { | 2684 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
2673 | if (end[1] != '\0') /* not ${var:NUM:} */ | 2685 | *p = '\0'; |
2674 | len = bb_strtou(end + 1, &end, 0); | 2686 | len = expand_and_evaluate_arith(exp_word, &errcode); |
2675 | else { | 2687 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); |
2676 | len = 0; | 2688 | |
2677 | end++; | 2689 | if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ |
2678 | } | 2690 | if (beg < 0) /* bash compat */ |
2679 | //bb_error_msg("len:%u end:'%s'", len, end); | 2691 | beg = 0; |
2680 | } | 2692 | debug_printf_varexp("from val:'%s'\n", val); |
2681 | if (*end == '\0') { | ||
2682 | //bb_error_msg("from val:'%s'", val); | ||
2683 | if (len == 0 || !val || beg >= strlen(val)) | 2693 | if (len == 0 || !val || beg >= strlen(val)) |
2684 | val = ""; | 2694 | val = ""; |
2685 | else | 2695 | else { |
2696 | /* Paranoia. What if user entered 9999999999999 | ||
2697 | * which fits in arith_t but not int? */ | ||
2698 | if (len >= INT_MAX) | ||
2699 | len = INT_MAX; | ||
2686 | val = to_be_freed = xstrndup(val + beg, len); | 2700 | val = to_be_freed = xstrndup(val + beg, len); |
2687 | //bb_error_msg("val:'%s'", val); | 2701 | } |
2702 | debug_printf_varexp("val:'%s'\n", val); | ||
2688 | } else | 2703 | } else |
2689 | #endif | 2704 | #endif |
2690 | { | 2705 | { |
2691 | die_if_script("malformed ${%s...}", var); | 2706 | die_if_script("malformed ${%s:...}", var); |
2692 | val = ""; | 2707 | val = ""; |
2693 | } | 2708 | } |
2694 | } else { /* one of "-=+?" */ | 2709 | } else { /* one of "-=+?" */ |
@@ -5891,21 +5906,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input) | |||
5891 | * echo $(echo 'TEST)' BEST) TEST) BEST | 5906 | * echo $(echo 'TEST)' BEST) TEST) BEST |
5892 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST | 5907 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST |
5893 | * | 5908 | * |
5894 | * Also adapted to eat ${var%...} constructs, since ... part | 5909 | * Also adapted to eat ${var%...} and $((...)) constructs, since ... part |
5895 | * can contain arbitrary constructs, just like $(cmd). | 5910 | * can contain arbitrary constructs, just like $(cmd). |
5911 | * In bash compat mode, it needs to also be able to stop on '}' or ':' | ||
5912 | * for ${var:N[:M]} parsing. | ||
5896 | */ | 5913 | */ |
5897 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 | 5914 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 |
5898 | static void add_till_closing_bracket(o_string *dest, struct in_str *input, char end_ch) | 5915 | static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) |
5899 | { | 5916 | { |
5917 | int ch; | ||
5900 | char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; | 5918 | char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; |
5901 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); | 5919 | #if ENABLE_HUSH_BASH_COMPAT |
5920 | char end_char2 = end_ch >> 8; | ||
5921 | #endif | ||
5922 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); | ||
5923 | |||
5902 | while (1) { | 5924 | while (1) { |
5903 | int ch = i_getch(input); | 5925 | ch = i_getch(input); |
5904 | if (ch == EOF) { | 5926 | if (ch == EOF) { |
5905 | syntax_error_unterm_ch(end_ch); | 5927 | syntax_error_unterm_ch(end_ch); |
5906 | /*xfunc_die(); - redundant */ | 5928 | /*xfunc_die(); - redundant */ |
5907 | } | 5929 | } |
5908 | if (ch == end_ch) { | 5930 | if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { |
5909 | if (!dbl) | 5931 | if (!dbl) |
5910 | break; | 5932 | break; |
5911 | /* we look for closing )) of $((EXPR)) */ | 5933 | /* we look for closing )) of $((EXPR)) */ |
@@ -5947,6 +5969,7 @@ static void add_till_closing_bracket(o_string *dest, struct in_str *input, char | |||
5947 | continue; | 5969 | continue; |
5948 | } | 5970 | } |
5949 | } | 5971 | } |
5972 | return ch; | ||
5950 | } | 5973 | } |
5951 | #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ | 5974 | #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ |
5952 | 5975 | ||
@@ -6033,22 +6056,45 @@ static int handle_dollar(o_string *as_string, | |||
6033 | break; | 6056 | break; |
6034 | 6057 | ||
6035 | if (!isalnum(ch) && ch != '_') { | 6058 | if (!isalnum(ch) && ch != '_') { |
6059 | unsigned end_ch; | ||
6060 | unsigned char last_ch; | ||
6036 | /* handle parameter expansions | 6061 | /* handle parameter expansions |
6037 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | 6062 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 |
6038 | */ | 6063 | */ |
6039 | if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */ | 6064 | if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */ |
6040 | goto bad_dollar_syntax; | 6065 | goto bad_dollar_syntax; |
6041 | /* Eat everything until closing '}' */ | ||
6042 | o_addchr(dest, ch); | 6066 | o_addchr(dest, ch); |
6067 | |||
6068 | /* Eat everything until closing '}' (or ':') */ | ||
6069 | end_ch = '}'; | ||
6070 | if (ENABLE_HUSH_BASH_COMPAT | ||
6071 | && ch == ':' | ||
6072 | && !strchr("%#:-=+?"+3, i_peek(input)) | ||
6073 | ) { | ||
6074 | /* It's ${var:N[:M]} thing */ | ||
6075 | end_ch = '}' * 0x100 + ':'; | ||
6076 | } | ||
6077 | again: | ||
6043 | if (!BB_MMU) | 6078 | if (!BB_MMU) |
6044 | pos = dest->length; | 6079 | pos = dest->length; |
6045 | add_till_closing_bracket(dest, input, '}'); | 6080 | last_ch = add_till_closing_bracket(dest, input, end_ch); |
6046 | #if !BB_MMU | ||
6047 | if (as_string) { | 6081 | if (as_string) { |
6048 | o_addstr(as_string, dest->data + pos); | 6082 | o_addstr(as_string, dest->data + pos); |
6049 | o_addchr(as_string, '}'); | 6083 | o_addchr(as_string, last_ch); |
6084 | } | ||
6085 | |||
6086 | if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { | ||
6087 | /* close the first block: */ | ||
6088 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
6089 | /* while parsing N from ${var:N[:M]}... */ | ||
6090 | if ((end_ch & 0xff) == last_ch) { | ||
6091 | /* ...got ':' - parse the rest */ | ||
6092 | end_ch = '}'; | ||
6093 | goto again; | ||
6094 | } | ||
6095 | /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */ | ||
6096 | o_addstr(dest, "999999999"); | ||
6050 | } | 6097 | } |
6051 | #endif | ||
6052 | break; | 6098 | break; |
6053 | } | 6099 | } |
6054 | } | 6100 | } |
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right index 6e3eb3ba6..53b8836ff 100644 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.right +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right | |||
@@ -39,3 +39,13 @@ f:1:2=|12| | |||
39 | f::2 =|01| | 39 | f::2 =|01| |
40 | f:1: =|| | 40 | f:1: =|| |
41 | f:: =|| | 41 | f:: =|| |
42 | Substrings with expressions | ||
43 | f =|01234567| | ||
44 | f:1+1:2+2 =|2345| | ||
45 | f:-1:2+2 =|01234567| | ||
46 | f:1:f =|1234567| | ||
47 | f:1:$f =|1234567| | ||
48 | f:1:${f} =|1234567| | ||
49 | f:1:${f:3:1} =|123| | ||
50 | f:1:1`echo 1`=|1| | ||
51 | Done | ||
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests index eedd435ed..a80523add 100755 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests | |||
@@ -1,8 +1,6 @@ | |||
1 | # first try some invalid patterns | ||
1 | # do all of these in subshells since it's supposed to error out | 2 | # do all of these in subshells since it's supposed to error out |
2 | |||
3 | export var=0123456789 | 3 | export var=0123456789 |
4 | |||
5 | # first try some invalid patterns | ||
6 | "$THIS_SH" -c 'echo ${:}' | 4 | "$THIS_SH" -c 'echo ${:}' |
7 | "$THIS_SH" -c 'echo ${::}' | 5 | "$THIS_SH" -c 'echo ${::}' |
8 | "$THIS_SH" -c 'echo ${:1}' | 6 | "$THIS_SH" -c 'echo ${:1}' |
@@ -15,44 +13,56 @@ export var=0123456789 | |||
15 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' | 13 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' |
16 | 14 | ||
17 | # now some valid ones | 15 | # now some valid ones |
18 | "$THIS_SH" -c 'set --; echo "1 =|${1}|"' | 16 | set --; echo "1 =|${1}|" |
19 | "$THIS_SH" -c 'set --; echo "1:1 =|${1:1}|"' | 17 | set --; echo "1:1 =|${1:1}|" |
20 | "$THIS_SH" -c 'set --; echo "1:1:2=|${1:1:2}|"' | 18 | set --; echo "1:1:2=|${1:1:2}|" |
21 | "$THIS_SH" -c 'set --; echo "1::2 =|${1::2}|"' | 19 | set --; echo "1::2 =|${1::2}|" |
22 | "$THIS_SH" -c 'set --; echo "1:1: =|${1:1:}|"' | 20 | set --; echo "1:1: =|${1:1:}|" |
23 | "$THIS_SH" -c 'set --; echo "1:: =|${1::}|"' | 21 | set --; echo "1:: =|${1::}|" |
24 | 22 | ||
25 | "$THIS_SH" -c 'set -- 0123; echo "1 =|${1}|"' | 23 | set -- 0123; echo "1 =|${1}|" |
26 | "$THIS_SH" -c 'set -- 0123; echo "1:1 =|${1:1}|"' | 24 | set -- 0123; echo "1:1 =|${1:1}|" |
27 | "$THIS_SH" -c 'set -- 0123; echo "1:1:2=|${1:1:2}|"' | 25 | set -- 0123; echo "1:1:2=|${1:1:2}|" |
28 | "$THIS_SH" -c 'set -- 0123; echo "1::2 =|${1::2}|"' | 26 | set -- 0123; echo "1::2 =|${1::2}|" |
29 | "$THIS_SH" -c 'set -- 0123; echo "1:1: =|${1:1:}|"' | 27 | set -- 0123; echo "1:1: =|${1:1:}|" |
30 | "$THIS_SH" -c 'set -- 0123; echo "1:: =|${1::}|"' | 28 | set -- 0123; echo "1:: =|${1::}|" |
31 | 29 | ||
32 | "$THIS_SH" -c 'unset f; echo "f =|$f|"' | 30 | unset f; echo "f =|$f|" |
33 | "$THIS_SH" -c 'unset f; echo "f:1 =|${f:1}|"' | 31 | unset f; echo "f:1 =|${f:1}|" |
34 | "$THIS_SH" -c 'unset f; echo "f:1:2=|${f:1:2}|"' | 32 | unset f; echo "f:1:2=|${f:1:2}|" |
35 | "$THIS_SH" -c 'unset f; echo "f::2 =|${f::2}|"' | 33 | unset f; echo "f::2 =|${f::2}|" |
36 | "$THIS_SH" -c 'unset f; echo "f:1: =|${f:1:}|"' | 34 | unset f; echo "f:1: =|${f:1:}|" |
37 | "$THIS_SH" -c 'unset f; echo "f:: =|${f::}|"' | 35 | unset f; echo "f:: =|${f::}|" |
38 | 36 | ||
39 | "$THIS_SH" -c 'f=; echo "f =|$f|"' | 37 | f=; echo "f =|$f|" |
40 | "$THIS_SH" -c 'f=; echo "f:1 =|${f:1}|"' | 38 | f=; echo "f:1 =|${f:1}|" |
41 | "$THIS_SH" -c 'f=; echo "f:1:2=|${f:1:2}|"' | 39 | f=; echo "f:1:2=|${f:1:2}|" |
42 | "$THIS_SH" -c 'f=; echo "f::2 =|${f::2}|"' | 40 | f=; echo "f::2 =|${f::2}|" |
43 | "$THIS_SH" -c 'f=; echo "f:1: =|${f:1:}|"' | 41 | f=; echo "f:1: =|${f:1:}|" |
44 | "$THIS_SH" -c 'f=; echo "f:: =|${f::}|"' | 42 | f=; echo "f:: =|${f::}|" |
45 | 43 | ||
46 | "$THIS_SH" -c 'f=a; echo "f =|$f|"' | 44 | f=a; echo "f =|$f|" |
47 | "$THIS_SH" -c 'f=a; echo "f:1 =|${f:1}|"' | 45 | f=a; echo "f:1 =|${f:1}|" |
48 | "$THIS_SH" -c 'f=a; echo "f:1:2=|${f:1:2}|"' | 46 | f=a; echo "f:1:2=|${f:1:2}|" |
49 | "$THIS_SH" -c 'f=a; echo "f::2 =|${f::2}|"' | 47 | f=a; echo "f::2 =|${f::2}|" |
50 | "$THIS_SH" -c 'f=a; echo "f:1: =|${f:1:}|"' | 48 | f=a; echo "f:1: =|${f:1:}|" |
51 | "$THIS_SH" -c 'f=a; echo "f:: =|${f::}|"' | 49 | f=a; echo "f:: =|${f::}|" |
52 | 50 | ||
53 | "$THIS_SH" -c 'f=0123456789; echo "f =|$f|"' | 51 | f=0123456789; echo "f =|$f|" |
54 | "$THIS_SH" -c 'f=0123456789; echo "f:1 =|${f:1}|"' | 52 | f=0123456789; echo "f:1 =|${f:1}|" |
55 | "$THIS_SH" -c 'f=0123456789; echo "f:1:2=|${f:1:2}|"' | 53 | f=0123456789; echo "f:1:2=|${f:1:2}|" |
56 | "$THIS_SH" -c 'f=0123456789; echo "f::2 =|${f::2}|"' | 54 | f=0123456789; echo "f::2 =|${f::2}|" |
57 | "$THIS_SH" -c 'f=0123456789; echo "f:1: =|${f:1:}|"' | 55 | f=0123456789; echo "f:1: =|${f:1:}|" |
58 | "$THIS_SH" -c 'f=0123456789; echo "f:: =|${f::}|"' | 56 | f=0123456789; echo "f:: =|${f::}|" |
57 | |||
58 | echo "Substrings with expressions" | ||
59 | f=01234567; echo 'f '"=|$f|" | ||
60 | f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" | ||
61 | f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" | ||
62 | f=01234567; echo 'f:1:f '"=|${f:1:f}|" | ||
63 | f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" | ||
64 | f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" | ||
65 | f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" | ||
66 | f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" | ||
67 | |||
68 | echo Done | ||