diff options
| author | Ron Yorston <rmy@pobox.com> | 2017-11-03 14:16:08 +0000 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2017-11-03 14:16:08 +0000 |
| commit | d6ce08aeb85b3698ddaa281016b70e16aeb9fb35 (patch) | |
| tree | 02ad9bc0684859515fe891f3d6b0a1086e0db156 /shell | |
| parent | ab450021a99ba66126cc6d668fb06ec3829a572b (diff) | |
| parent | a5060b8364faa7c677c8950f1315c451403b0660 (diff) | |
| download | busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.gz busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.bz2 busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.zip | |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 105 | ||||
| -rw-r--r-- | shell/ash_test/ash-parsing/comment2.right | 4 | ||||
| -rwxr-xr-x | shell/ash_test/ash-parsing/comment2.tests | 13 | ||||
| -rw-r--r-- | shell/ash_test/ash-standalone/nofork_env.right | 9 | ||||
| -rwxr-xr-x | shell/ash_test/ash-standalone/nofork_env.tests | 15 | ||||
| -rw-r--r-- | shell/hush.c | 17 | ||||
| -rw-r--r-- | shell/hush_test/hush-parsing/comment2.right | 4 | ||||
| -rwxr-xr-x | shell/hush_test/hush-parsing/comment2.tests | 13 | ||||
| -rw-r--r-- | shell/hush_test/hush-standalone/nofork_env.right | 9 | ||||
| -rwxr-xr-x | shell/hush_test/hush-standalone/nofork_env.tests | 15 |
10 files changed, 179 insertions, 25 deletions
diff --git a/shell/ash.c b/shell/ash.c index 0d65a225b..81845dc60 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -1399,16 +1399,9 @@ ash_msg_and_raise_error(const char *msg, ...) | |||
| 1399 | } | 1399 | } |
| 1400 | 1400 | ||
| 1401 | /* | 1401 | /* |
| 1402 | * Use '%m' to append error string on platforms that support it, '%s' and | ||
| 1403 | * strerror() on those that don't. | ||
| 1404 | * | ||
| 1405 | * 'fmt' must be a string literal. | 1402 | * 'fmt' must be a string literal. |
| 1406 | */ | 1403 | */ |
| 1407 | #ifdef HAVE_PRINTF_PERCENTM | 1404 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": "STRERROR_FMT, ##__VA_ARGS__ STRERROR_ERRNO) |
| 1408 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %m", ##__VA_ARGS__) | ||
| 1409 | #else | ||
| 1410 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %s", ##__VA_ARGS__, strerror(errno)) | ||
| 1411 | #endif | ||
| 1412 | 1405 | ||
| 1413 | static void raise_error_syntax(const char *) NORETURN; | 1406 | static void raise_error_syntax(const char *) NORETURN; |
| 1414 | static void | 1407 | static void |
| @@ -2474,8 +2467,11 @@ listsetvar(struct strlist *list_set_var, int flags) | |||
| 2474 | /* | 2467 | /* |
| 2475 | * Generate a list of variables satisfying the given conditions. | 2468 | * Generate a list of variables satisfying the given conditions. |
| 2476 | */ | 2469 | */ |
| 2470 | #if !ENABLE_FEATURE_SH_NOFORK | ||
| 2471 | # define listvars(on, off, lp, end) listvars(on, off, end) | ||
| 2472 | #endif | ||
| 2477 | static char ** | 2473 | static char ** |
| 2478 | listvars(int on, int off, char ***end) | 2474 | listvars(int on, int off, struct strlist *lp, char ***end) |
| 2479 | { | 2475 | { |
| 2480 | struct var **vpp; | 2476 | struct var **vpp; |
| 2481 | struct var *vp; | 2477 | struct var *vp; |
| @@ -2488,12 +2484,40 @@ listvars(int on, int off, char ***end) | |||
| 2488 | do { | 2484 | do { |
| 2489 | for (vp = *vpp; vp; vp = vp->next) { | 2485 | for (vp = *vpp; vp; vp = vp->next) { |
| 2490 | if ((vp->flags & mask) == on) { | 2486 | if ((vp->flags & mask) == on) { |
| 2487 | #if ENABLE_FEATURE_SH_NOFORK | ||
| 2488 | /* If variable with the same name is both | ||
| 2489 | * exported and temporarily set for a command: | ||
| 2490 | * export ZVAR=5 | ||
| 2491 | * ZVAR=6 printenv | ||
| 2492 | * then "ZVAR=6" will be both in vartab and | ||
| 2493 | * lp lists. Do not pass it twice to printenv. | ||
| 2494 | */ | ||
| 2495 | struct strlist *lp1 = lp; | ||
| 2496 | while (lp1) { | ||
| 2497 | if (strcmp(lp1->text, vp->var_text) == 0) | ||
| 2498 | goto skip; | ||
| 2499 | lp1 = lp1->next; | ||
| 2500 | } | ||
| 2501 | #endif | ||
| 2491 | if (ep == stackstrend()) | 2502 | if (ep == stackstrend()) |
| 2492 | ep = growstackstr(); | 2503 | ep = growstackstr(); |
| 2493 | *ep++ = (char*)vp->var_text; | 2504 | *ep++ = (char*)vp->var_text; |
| 2505 | #if ENABLE_FEATURE_SH_NOFORK | ||
| 2506 | skip: ; | ||
| 2507 | #endif | ||
| 2494 | } | 2508 | } |
| 2495 | } | 2509 | } |
| 2496 | } while (++vpp < vartab + VTABSIZE); | 2510 | } while (++vpp < vartab + VTABSIZE); |
| 2511 | |||
| 2512 | #if ENABLE_FEATURE_SH_NOFORK | ||
| 2513 | while (lp) { | ||
| 2514 | if (ep == stackstrend()) | ||
| 2515 | ep = growstackstr(); | ||
| 2516 | *ep++ = lp->text; | ||
| 2517 | lp = lp->next; | ||
| 2518 | } | ||
| 2519 | #endif | ||
| 2520 | |||
| 2497 | if (ep == stackstrend()) | 2521 | if (ep == stackstrend()) |
| 2498 | ep = growstackstr(); | 2522 | ep = growstackstr(); |
| 2499 | if (end) | 2523 | if (end) |
| @@ -8250,7 +8274,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
| 8250 | int exerrno; | 8274 | int exerrno; |
| 8251 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ | 8275 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ |
| 8252 | 8276 | ||
| 8253 | envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); | 8277 | envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); |
| 8254 | if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\'))) | 8278 | if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\'))) |
| 8255 | #if ENABLE_FEATURE_SH_STANDALONE | 8279 | #if ENABLE_FEATURE_SH_STANDALONE |
| 8256 | || (applet_no = find_applet_by_name(prog)) >= 0 | 8280 | || (applet_no = find_applet_by_name(prog)) >= 0 |
| @@ -10395,7 +10419,11 @@ evalcommand(union node *cmd, int flags) | |||
| 10395 | /* find_command() encodes applet_no as (-2 - applet_no) */ | 10419 | /* find_command() encodes applet_no as (-2 - applet_no) */ |
| 10396 | int applet_no = (- cmdentry.u.index - 2); | 10420 | int applet_no = (- cmdentry.u.index - 2); |
| 10397 | if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { | 10421 | if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { |
| 10398 | listsetvar(varlist.list, VEXPORT|VSTACK); | 10422 | char **sv_environ; |
| 10423 | |||
| 10424 | INT_OFF; | ||
| 10425 | sv_environ = environ; | ||
| 10426 | environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL); | ||
| 10399 | /* | 10427 | /* |
| 10400 | * Run <applet>_main(). | 10428 | * Run <applet>_main(). |
| 10401 | * Signals (^C) can't interrupt here. | 10429 | * Signals (^C) can't interrupt here. |
| @@ -10404,8 +10432,8 @@ evalcommand(union node *cmd, int flags) | |||
| 10404 | * and/or wait for user input ineligible for NOFORK: | 10432 | * and/or wait for user input ineligible for NOFORK: |
| 10405 | * for example, "yes" or "rm" (rm -i waits for input). | 10433 | * for example, "yes" or "rm" (rm -i waits for input). |
| 10406 | */ | 10434 | */ |
| 10407 | INT_OFF; | ||
| 10408 | status = run_nofork_applet(applet_no, argv); | 10435 | status = run_nofork_applet(applet_no, argv); |
| 10436 | environ = sv_environ; | ||
| 10409 | /* | 10437 | /* |
| 10410 | * Try enabling NOFORK for "yes" applet. | 10438 | * Try enabling NOFORK for "yes" applet. |
| 10411 | * ^C _will_ stop it (write returns EINTR), | 10439 | * ^C _will_ stop it (write returns EINTR), |
| @@ -11344,7 +11372,7 @@ showvars(const char *sep_prefix, int on, int off) | |||
| 11344 | const char *sep; | 11372 | const char *sep; |
| 11345 | char **ep, **epend; | 11373 | char **ep, **epend; |
| 11346 | 11374 | ||
| 11347 | ep = listvars(on, off, &epend); | 11375 | ep = listvars(on, off, /*strlist:*/ NULL, &epend); |
| 11348 | qsort(ep, epend - ep, sizeof(char *), vpcmp); | 11376 | qsort(ep, epend - ep, sizeof(char *), vpcmp); |
| 11349 | 11377 | ||
| 11350 | sep = *sep_prefix ? " " : sep_prefix; | 11378 | sep = *sep_prefix ? " " : sep_prefix; |
| @@ -11353,9 +11381,17 @@ showvars(const char *sep_prefix, int on, int off) | |||
| 11353 | const char *p; | 11381 | const char *p; |
| 11354 | const char *q; | 11382 | const char *q; |
| 11355 | 11383 | ||
| 11356 | p = strchrnul(*ep, '='); | 11384 | p = endofname(*ep); |
| 11385 | /* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this | ||
| 11386 | * makes "export -p" to have output not suitable for "eval": | ||
| 11387 | * import os | ||
| 11388 | * os.environ["test-test"]="test" | ||
| 11389 | * if os.fork() == 0: | ||
| 11390 | * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this | ||
| 11391 | * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) | ||
| 11392 | */ | ||
| 11357 | q = nullstr; | 11393 | q = nullstr; |
| 11358 | if (*p) | 11394 | if (*p == '=') |
| 11359 | q = single_quote(++p); | 11395 | q = single_quote(++p); |
| 11360 | out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); | 11396 | out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); |
| 11361 | } | 11397 | } |
| @@ -13146,7 +13182,24 @@ expandstr(const char *ps, int syntax_type) | |||
| 13146 | 13182 | ||
| 13147 | saveprompt = doprompt; | 13183 | saveprompt = doprompt; |
| 13148 | doprompt = 0; | 13184 | doprompt = 0; |
| 13149 | readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); | 13185 | |
| 13186 | /* readtoken1() might die horribly. | ||
| 13187 | * Try a prompt with syntactically wrong command: | ||
| 13188 | * PS1='$(date "+%H:%M:%S) > ' | ||
| 13189 | */ | ||
| 13190 | { | ||
| 13191 | volatile int saveint; | ||
| 13192 | struct jmploc *volatile savehandler = exception_handler; | ||
| 13193 | struct jmploc jmploc; | ||
| 13194 | SAVE_INT(saveint); | ||
| 13195 | if (setjmp(jmploc.loc) == 0) { | ||
| 13196 | exception_handler = &jmploc; | ||
| 13197 | readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); | ||
| 13198 | } | ||
| 13199 | exception_handler = savehandler; | ||
| 13200 | RESTORE_INT(saveint); | ||
| 13201 | } | ||
| 13202 | |||
| 13150 | doprompt = saveprompt; | 13203 | doprompt = saveprompt; |
| 13151 | 13204 | ||
| 13152 | popfile(); | 13205 | popfile(); |
| @@ -13215,7 +13268,7 @@ evalstring(char *s, int flags) | |||
| 13215 | 13268 | ||
| 13216 | exception_handler = savehandler; | 13269 | exception_handler = savehandler; |
| 13217 | if (ex) | 13270 | if (ex) |
| 13218 | longjmp(exception_handler->loc, ex); | 13271 | longjmp(exception_handler->loc, ex); |
| 13219 | 13272 | ||
| 13220 | return status; | 13273 | return status; |
| 13221 | } | 13274 | } |
| @@ -14069,8 +14122,8 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 14069 | } | 14122 | } |
| 14070 | } else { | 14123 | } else { |
| 14071 | char *modestr = *argptr; | 14124 | char *modestr = *argptr; |
| 14072 | /* numeric umasks are taken as-is */ | 14125 | /* numeric umasks are taken as-is */ |
| 14073 | /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */ | 14126 | /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */ |
| 14074 | if (!isdigit(modestr[0])) | 14127 | if (!isdigit(modestr[0])) |
| 14075 | mask ^= 0777; | 14128 | mask ^= 0777; |
| 14076 | mask = bb_parse_mode(modestr, mask); | 14129 | mask = bb_parse_mode(modestr, mask); |
| @@ -14236,8 +14289,18 @@ init(void) | |||
| 14236 | } | 14289 | } |
| 14237 | #endif | 14290 | #endif |
| 14238 | for (envp = environ; envp && *envp; envp++) { | 14291 | for (envp = environ; envp && *envp; envp++) { |
| 14239 | p = endofname(*envp); | 14292 | /* Used to have |
| 14240 | if (p != *envp && *p == '=') { | 14293 | * p = endofname(*envp); |
| 14294 | * if (p != *envp && *p == '=') { | ||
| 14295 | * here to weed out badly-named variables, but this breaks | ||
| 14296 | * scenarios where people do want them passed to children: | ||
| 14297 | * import os | ||
| 14298 | * os.environ["test-test"]="test" | ||
| 14299 | * if os.fork() == 0: | ||
| 14300 | * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this | ||
| 14301 | * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this | ||
| 14302 | */ | ||
| 14303 | if (strchr(*envp, '=')) { | ||
| 14241 | setvareq(*envp, VEXPORT|VTEXTFIXED); | 14304 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
| 14242 | } | 14305 | } |
| 14243 | } | 14306 | } |
diff --git a/shell/ash_test/ash-parsing/comment2.right b/shell/ash_test/ash-parsing/comment2.right new file mode 100644 index 000000000..ee6e49a5a --- /dev/null +++ b/shell/ash_test/ash-parsing/comment2.right | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | Ok1 | ||
| 2 | Ok2 | ||
| 3 | Ok5 | ||
| 4 | Ok6 | ||
diff --git a/shell/ash_test/ash-parsing/comment2.tests b/shell/ash_test/ash-parsing/comment2.tests new file mode 100755 index 000000000..b7adad96a --- /dev/null +++ b/shell/ash_test/ash-parsing/comment2.tests | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | echo "`echo Ok1 #comment is ignored`" | ||
| 2 | echo `echo Ok2 #comment is ignored` | ||
| 3 | # | ||
| 4 | # Surprisingly, bash does not handle comments in $() | ||
| 5 | # the same way as in ``. "#" causes the rest of the line, _including_ )", | ||
| 6 | # to be ignored. These lines would cause an error: | ||
| 7 | #echo "$(echo Ok3 #comment is ignored)" | ||
| 8 | #echo $(echo Ok4 #comment is ignored) | ||
| 9 | # | ||
| 10 | echo "$(echo Ok5 #comment is ignored | ||
| 11 | )" | ||
| 12 | echo $(echo Ok6 #comment is ignored | ||
| 13 | ) | ||
diff --git a/shell/ash_test/ash-standalone/nofork_env.right b/shell/ash_test/ash-standalone/nofork_env.right new file mode 100644 index 000000000..3f16ff458 --- /dev/null +++ b/shell/ash_test/ash-standalone/nofork_env.right | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | ZVAR=1 | ||
| 2 | ZVAR=2 | ||
| 3 | ZVAR=3 | ||
| 4 | ZVAR=4 | ||
| 5 | ZVAR=5 | ||
| 6 | ZVAR=6 | ||
| 7 | ZVAR=7 | ||
| 8 | ZVAR=8 | ||
| 9 | Ok:0 | ||
diff --git a/shell/ash_test/ash-standalone/nofork_env.tests b/shell/ash_test/ash-standalone/nofork_env.tests new file mode 100755 index 000000000..111e564d2 --- /dev/null +++ b/shell/ash_test/ash-standalone/nofork_env.tests | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # ash had a bug where NOFORKed applet (env/printenv) was not seeing new exported variables | ||
| 2 | |||
| 3 | (export ZVAR=1; printenv) | grep ^ZVAR= | ||
| 4 | (ZVAR=2 printenv) | grep ^ZVAR= | ||
| 5 | |||
| 6 | (export ZVAR=3; env) | grep ^ZVAR= | ||
| 7 | (ZVAR=4 env) | grep ^ZVAR= | ||
| 8 | |||
| 9 | export ZVAR=5; printenv | grep ^ZVAR= | ||
| 10 | ZVAR=6 printenv | grep ^ZVAR= | ||
| 11 | |||
| 12 | export ZVAR=7; env | grep ^ZVAR= | ||
| 13 | ZVAR=8 env | grep ^ZVAR= | ||
| 14 | |||
| 15 | echo Ok:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index d27550ba0..708555ac4 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -5140,14 +5140,23 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5140 | case '#': | 5140 | case '#': |
| 5141 | if (dest.length == 0 && !dest.has_quoted_part) { | 5141 | if (dest.length == 0 && !dest.has_quoted_part) { |
| 5142 | /* skip "#comment" */ | 5142 | /* skip "#comment" */ |
| 5143 | /* note: we do not add it to &ctx.as_string */ | ||
| 5144 | /* TODO: in bash: | ||
| 5145 | * comment inside $() goes to the next \n, even inside quoted string (!): | ||
| 5146 | * cmd "$(cmd2 #comment)" - syntax error | ||
| 5147 | * cmd "`cmd2 #comment`" - ok | ||
| 5148 | * We accept both (comment ends where command subst ends, in both cases). | ||
| 5149 | */ | ||
| 5143 | while (1) { | 5150 | while (1) { |
| 5144 | ch = i_peek(input); | 5151 | ch = i_peek(input); |
| 5145 | if (ch == EOF || ch == '\n') | 5152 | if (ch == '\n') { |
| 5153 | nommu_addchr(&ctx.as_string, '\n'); | ||
| 5154 | break; | ||
| 5155 | } | ||
| 5156 | ch = i_getch(input); | ||
| 5157 | if (ch == EOF) | ||
| 5146 | break; | 5158 | break; |
| 5147 | i_getch(input); | ||
| 5148 | /* note: we do not add it to &ctx.as_string */ | ||
| 5149 | } | 5159 | } |
| 5150 | nommu_addchr(&ctx.as_string, '\n'); | ||
| 5151 | continue; /* back to top of while (1) */ | 5160 | continue; /* back to top of while (1) */ |
| 5152 | } | 5161 | } |
| 5153 | break; | 5162 | break; |
diff --git a/shell/hush_test/hush-parsing/comment2.right b/shell/hush_test/hush-parsing/comment2.right new file mode 100644 index 000000000..ee6e49a5a --- /dev/null +++ b/shell/hush_test/hush-parsing/comment2.right | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | Ok1 | ||
| 2 | Ok2 | ||
| 3 | Ok5 | ||
| 4 | Ok6 | ||
diff --git a/shell/hush_test/hush-parsing/comment2.tests b/shell/hush_test/hush-parsing/comment2.tests new file mode 100755 index 000000000..b7adad96a --- /dev/null +++ b/shell/hush_test/hush-parsing/comment2.tests | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | echo "`echo Ok1 #comment is ignored`" | ||
| 2 | echo `echo Ok2 #comment is ignored` | ||
| 3 | # | ||
| 4 | # Surprisingly, bash does not handle comments in $() | ||
| 5 | # the same way as in ``. "#" causes the rest of the line, _including_ )", | ||
| 6 | # to be ignored. These lines would cause an error: | ||
| 7 | #echo "$(echo Ok3 #comment is ignored)" | ||
| 8 | #echo $(echo Ok4 #comment is ignored) | ||
| 9 | # | ||
| 10 | echo "$(echo Ok5 #comment is ignored | ||
| 11 | )" | ||
| 12 | echo $(echo Ok6 #comment is ignored | ||
| 13 | ) | ||
diff --git a/shell/hush_test/hush-standalone/nofork_env.right b/shell/hush_test/hush-standalone/nofork_env.right new file mode 100644 index 000000000..3f16ff458 --- /dev/null +++ b/shell/hush_test/hush-standalone/nofork_env.right | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | ZVAR=1 | ||
| 2 | ZVAR=2 | ||
| 3 | ZVAR=3 | ||
| 4 | ZVAR=4 | ||
| 5 | ZVAR=5 | ||
| 6 | ZVAR=6 | ||
| 7 | ZVAR=7 | ||
| 8 | ZVAR=8 | ||
| 9 | Ok:0 | ||
diff --git a/shell/hush_test/hush-standalone/nofork_env.tests b/shell/hush_test/hush-standalone/nofork_env.tests new file mode 100755 index 000000000..111e564d2 --- /dev/null +++ b/shell/hush_test/hush-standalone/nofork_env.tests | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # ash had a bug where NOFORKed applet (env/printenv) was not seeing new exported variables | ||
| 2 | |||
| 3 | (export ZVAR=1; printenv) | grep ^ZVAR= | ||
| 4 | (ZVAR=2 printenv) | grep ^ZVAR= | ||
| 5 | |||
| 6 | (export ZVAR=3; env) | grep ^ZVAR= | ||
| 7 | (ZVAR=4 env) | grep ^ZVAR= | ||
| 8 | |||
| 9 | export ZVAR=5; printenv | grep ^ZVAR= | ||
| 10 | ZVAR=6 printenv | grep ^ZVAR= | ||
| 11 | |||
| 12 | export ZVAR=7; env | grep ^ZVAR= | ||
| 13 | ZVAR=8 env | grep ^ZVAR= | ||
| 14 | |||
| 15 | echo Ok:$? | ||
