diff options
author | Ron Yorston <rmy@pobox.com> | 2020-07-23 08:32:27 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-05 23:37:19 +0200 |
commit | a1b0d3856d9a0419cb74bf4c87525265871b5868 (patch) | |
tree | 1c3dface41c9c1f4d23a42819c9e6c2101e4f303 | |
parent | 8c1f8aa016faee3fa151d134c3544b2dd5bab832 (diff) | |
download | busybox-w32-a1b0d3856d9a0419cb74bf4c87525265871b5868.tar.gz busybox-w32-a1b0d3856d9a0419cb74bf4c87525265871b5868.tar.bz2 busybox-w32-a1b0d3856d9a0419cb74bf4c87525265871b5868.zip |
ash: add process substitution in bash-compatibility mode
Process substitution is a Korn shell feature that's also available
in bash and some other shells. This patch implements process
substitution in ash when ASH_BASH_COMPAT is enabled.
function old new delta
argstr 1386 1522 +136
strtodest - 52 +52
readtoken1 3346 3392 +46
.rodata 183206 183250 +44
unwindredir - 28 +28
cmdloop 365 372 +7
static.spclchars 10 12 +2
cmdputs 380 367 -13
exitreset 86 69 -17
evalcommand 1754 1737 -17
varvalue 675 634 -41
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 5/4 up/down: 315/-88) Total: 227 bytes
text data bss dec hex filename
953967 4219 1904 960090 ea65a busybox_old
954192 4219 1904 960315 ea73b busybox_unstripped
v2: Replace array of file descriptors with a linked list.
Include tests that were unaccountably omitted from v1.
v3: Update linked list code to the intended version.
v4: Change order of conditional code in cmdputs().
v5: Use existing popredir() mechanism to manage file descriptors.
v6: Rebase to latest version of BusyBox ash. Reduce code churn.
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | include/platform.h | 2 | ||||
-rw-r--r-- | shell/ash.c | 160 | ||||
-rw-r--r-- | shell/ash_test/ash-psubst/bash_procsub.right | 9 | ||||
-rwxr-xr-x | shell/ash_test/ash-psubst/bash_procsub.tests | 33 |
4 files changed, 187 insertions, 17 deletions
diff --git a/include/platform.h b/include/platform.h index 4633b2507..9e1fb047d 100644 --- a/include/platform.h +++ b/include/platform.h | |||
@@ -426,6 +426,8 @@ typedef unsigned smalluint; | |||
426 | #define HAVE_SYS_STATFS_H 1 | 426 | #define HAVE_SYS_STATFS_H 1 |
427 | #define HAVE_PRINTF_PERCENTM 1 | 427 | #define HAVE_PRINTF_PERCENTM 1 |
428 | #define HAVE_WAIT3 1 | 428 | #define HAVE_WAIT3 1 |
429 | #define HAVE_DEV_FD 1 | ||
430 | #define DEV_FD_PREFIX "/dev/fd/" | ||
429 | 431 | ||
430 | #if defined(__UCLIBC__) | 432 | #if defined(__UCLIBC__) |
431 | # if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) | 433 | # if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) |
diff --git a/shell/ash.c b/shell/ash.c index 6a16833b1..05c47950f 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -229,6 +229,14 @@ | |||
229 | #define BASH_READ_D ENABLE_ASH_BASH_COMPAT | 229 | #define BASH_READ_D ENABLE_ASH_BASH_COMPAT |
230 | #define IF_BASH_READ_D IF_ASH_BASH_COMPAT | 230 | #define IF_BASH_READ_D IF_ASH_BASH_COMPAT |
231 | #define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT | 231 | #define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT |
232 | /* <(...) and >(...) */ | ||
233 | #if HAVE_DEV_FD | ||
234 | # define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT | ||
235 | # define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT | ||
236 | #else | ||
237 | # define BASH_PROCESS_SUBST 0 | ||
238 | # define IF_BASH_PROCESS_SUBST(...) | ||
239 | #endif | ||
232 | 240 | ||
233 | #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 | 241 | #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 |
234 | /* Bionic at least up to version 24 has no glob() */ | 242 | /* Bionic at least up to version 24 has no glob() */ |
@@ -804,6 +812,12 @@ out2str(const char *p) | |||
804 | #define CTLENDARI ((unsigned char)'\207') | 812 | #define CTLENDARI ((unsigned char)'\207') |
805 | #define CTLQUOTEMARK ((unsigned char)'\210') | 813 | #define CTLQUOTEMARK ((unsigned char)'\210') |
806 | #define CTL_LAST CTLQUOTEMARK | 814 | #define CTL_LAST CTLQUOTEMARK |
815 | #if BASH_PROCESS_SUBST | ||
816 | # define CTLTOPROC ((unsigned char)'\211') | ||
817 | # define CTLFROMPROC ((unsigned char)'\212') | ||
818 | # undef CTL_LAST | ||
819 | # define CTL_LAST CTLFROMPROC | ||
820 | #endif | ||
807 | 821 | ||
808 | /* variable substitution byte (follows CTLVAR) */ | 822 | /* variable substitution byte (follows CTLVAR) */ |
809 | #define VSTYPE 0x0f /* type of variable substitution */ | 823 | #define VSTYPE 0x0f /* type of variable substitution */ |
@@ -1075,6 +1089,10 @@ trace_puts_quoted(char *s) | |||
1075 | case CTLESC: c = 'e'; goto backslash; | 1089 | case CTLESC: c = 'e'; goto backslash; |
1076 | case CTLVAR: c = 'v'; goto backslash; | 1090 | case CTLVAR: c = 'v'; goto backslash; |
1077 | case CTLBACKQ: c = 'q'; goto backslash; | 1091 | case CTLBACKQ: c = 'q'; goto backslash; |
1092 | #if BASH_PROCESS_SUBST | ||
1093 | case CTLTOPROC: c = 'p'; goto backslash; | ||
1094 | case CTLFROMPROC: c = 'P'; goto backslash; | ||
1095 | #endif | ||
1078 | backslash: | 1096 | backslash: |
1079 | putc('\\', tracefile); | 1097 | putc('\\', tracefile); |
1080 | putc(c, tracefile); | 1098 | putc(c, tracefile); |
@@ -1236,8 +1254,17 @@ sharg(union node *arg, FILE *fp) | |||
1236 | case CTLENDVAR: | 1254 | case CTLENDVAR: |
1237 | putc('}', fp); | 1255 | putc('}', fp); |
1238 | break; | 1256 | break; |
1257 | #if BASH_PROCESS_SUBST | ||
1258 | case CTLTOPROC: | ||
1259 | putc('>', fp); | ||
1260 | goto backq; | ||
1261 | case CTLFROMPROC: | ||
1262 | putc('<', fp); | ||
1263 | goto backq; | ||
1264 | #endif | ||
1239 | case CTLBACKQ: | 1265 | case CTLBACKQ: |
1240 | putc('$', fp); | 1266 | putc('$', fp); |
1267 | IF_BASH_PROCESS_SUBST(backq:) | ||
1241 | putc('(', fp); | 1268 | putc('(', fp); |
1242 | shtree(bqlist->n, -1, NULL, fp); | 1269 | shtree(bqlist->n, -1, NULL, fp); |
1243 | putc(')', fp); | 1270 | putc(')', fp); |
@@ -3234,8 +3261,13 @@ static const uint8_t syntax_index_table[] ALIGN1 = { | |||
3234 | /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, | 3261 | /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, |
3235 | /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, | 3262 | /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, |
3236 | /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, | 3263 | /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, |
3264 | #if BASH_PROCESS_SUBST | ||
3265 | /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL, | ||
3266 | /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL, | ||
3267 | #else | ||
3237 | /* 137 */ CWORD_CWORD_CWORD_CWORD, | 3268 | /* 137 */ CWORD_CWORD_CWORD_CWORD, |
3238 | /* 138 */ CWORD_CWORD_CWORD_CWORD, | 3269 | /* 138 */ CWORD_CWORD_CWORD_CWORD, |
3270 | #endif | ||
3239 | /* 139 */ CWORD_CWORD_CWORD_CWORD, | 3271 | /* 139 */ CWORD_CWORD_CWORD_CWORD, |
3240 | /* 140 */ CWORD_CWORD_CWORD_CWORD, | 3272 | /* 140 */ CWORD_CWORD_CWORD_CWORD, |
3241 | /* 141 */ CWORD_CWORD_CWORD_CWORD, | 3273 | /* 141 */ CWORD_CWORD_CWORD_CWORD, |
@@ -4849,9 +4881,24 @@ cmdputs(const char *s) | |||
4849 | quoted >>= 1; | 4881 | quoted >>= 1; |
4850 | subtype = 0; | 4882 | subtype = 0; |
4851 | goto dostr; | 4883 | goto dostr; |
4884 | #if BASH_PROCESS_SUBST | ||
4885 | case CTLBACKQ: | ||
4886 | c = '$'; | ||
4887 | str = "(...)"; | ||
4888 | break; | ||
4889 | case CTLTOPROC: | ||
4890 | c = '>'; | ||
4891 | str = "(...)"; | ||
4892 | break; | ||
4893 | case CTLFROMPROC: | ||
4894 | c = '<'; | ||
4895 | str = "(...)"; | ||
4896 | break; | ||
4897 | #else | ||
4852 | case CTLBACKQ: | 4898 | case CTLBACKQ: |
4853 | str = "$(...)"; | 4899 | str = "$(...)"; |
4854 | goto dostr; | 4900 | goto dostr; |
4901 | #endif | ||
4855 | #if ENABLE_FEATURE_SH_MATH | 4902 | #if ENABLE_FEATURE_SH_MATH |
4856 | case CTLARI: | 4903 | case CTLARI: |
4857 | str = "$(("; | 4904 | str = "$(("; |
@@ -5891,6 +5938,21 @@ redirectsafe(union node *redir, int flags) | |||
5891 | return err; | 5938 | return err; |
5892 | } | 5939 | } |
5893 | 5940 | ||
5941 | #if BASH_PROCESS_SUBST | ||
5942 | static void | ||
5943 | pushfd(int fd) | ||
5944 | { | ||
5945 | struct redirtab *sv; | ||
5946 | |||
5947 | sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0])); | ||
5948 | sv->pair_count = 1; | ||
5949 | sv->two_fd[0].orig_fd = fd; | ||
5950 | sv->two_fd[0].moved_to = CLOSED; | ||
5951 | sv->next = redirlist; | ||
5952 | redirlist = sv; | ||
5953 | } | ||
5954 | #endif | ||
5955 | |||
5894 | static struct redirtab* | 5956 | static struct redirtab* |
5895 | pushredir(union node *redir) | 5957 | pushredir(union node *redir) |
5896 | { | 5958 | { |
@@ -6529,10 +6591,20 @@ evaltreenr(union node *n, int flags) | |||
6529 | } | 6591 | } |
6530 | 6592 | ||
6531 | static void FAST_FUNC | 6593 | static void FAST_FUNC |
6532 | evalbackcmd(union node *n, struct backcmd *result) | 6594 | evalbackcmd(union node *n, struct backcmd *result |
6595 | IF_BASH_PROCESS_SUBST(, int ctl)) | ||
6533 | { | 6596 | { |
6534 | int pip[2]; | 6597 | int pip[2]; |
6535 | struct job *jp; | 6598 | struct job *jp; |
6599 | #if BASH_PROCESS_SUBST | ||
6600 | /* determine end of pipe used by parent (ip) and child (ic) */ | ||
6601 | const int ip = (ctl == CTLTOPROC); | ||
6602 | const int ic = !(ctl == CTLTOPROC); | ||
6603 | #else | ||
6604 | const int ctl = CTLBACKQ; | ||
6605 | const int ip = 0; | ||
6606 | const int ic = 1; | ||
6607 | #endif | ||
6536 | 6608 | ||
6537 | result->fd = -1; | 6609 | result->fd = -1; |
6538 | result->buf = NULL; | 6610 | result->buf = NULL; |
@@ -6544,15 +6616,17 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6544 | 6616 | ||
6545 | if (pipe(pip) < 0) | 6617 | if (pipe(pip) < 0) |
6546 | ash_msg_and_raise_perror("can't create pipe"); | 6618 | ash_msg_and_raise_perror("can't create pipe"); |
6547 | jp = makejob(/*n,*/ 1); | 6619 | /* process substitution uses NULL job/node, like openhere() */ |
6548 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | 6620 | jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; |
6621 | if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) { | ||
6549 | /* child */ | 6622 | /* child */ |
6550 | FORCE_INT_ON; | 6623 | FORCE_INT_ON; |
6551 | close(pip[0]); | 6624 | close(pip[ip]); |
6552 | if (pip[1] != 1) { | 6625 | /* ic is index of child end of pipe *and* fd to connect it to */ |
6553 | /*close(1);*/ | 6626 | if (pip[ic] != ic) { |
6554 | dup2_or_raise(pip[1], 1); | 6627 | /*close(ic);*/ |
6555 | close(pip[1]); | 6628 | dup2_or_raise(pip[ic], ic); |
6629 | close(pip[ic]); | ||
6556 | } | 6630 | } |
6557 | /* TODO: eflag clearing makes the following not abort: | 6631 | /* TODO: eflag clearing makes the following not abort: |
6558 | * ash -c 'set -e; z=$(false;echo foo); echo $z' | 6632 | * ash -c 'set -e; z=$(false;echo foo); echo $z' |
@@ -6568,8 +6642,18 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6568 | /* NOTREACHED */ | 6642 | /* NOTREACHED */ |
6569 | } | 6643 | } |
6570 | /* parent */ | 6644 | /* parent */ |
6571 | close(pip[1]); | 6645 | #if BASH_PROCESS_SUBST |
6572 | result->fd = pip[0]; | 6646 | if (ctl != CTLBACKQ) { |
6647 | int fd = fcntl(pip[ip], F_DUPFD, 64); | ||
6648 | if (fd > 0) { | ||
6649 | close(pip[ip]); | ||
6650 | pip[ip] = fd; | ||
6651 | } | ||
6652 | pushfd(pip[ip]); | ||
6653 | } | ||
6654 | #endif | ||
6655 | close(pip[ic]); | ||
6656 | result->fd = pip[ip]; | ||
6573 | result->jp = jp; | 6657 | result->jp = jp; |
6574 | 6658 | ||
6575 | out: | 6659 | out: |
@@ -6581,8 +6665,11 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6581 | * Expand stuff in backwards quotes. | 6665 | * Expand stuff in backwards quotes. |
6582 | */ | 6666 | */ |
6583 | static void | 6667 | static void |
6584 | expbackq(union node *cmd, int flag) | 6668 | expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl)) |
6585 | { | 6669 | { |
6670 | #if !BASH_PROCESS_SUBST | ||
6671 | const int ctl = CTLBACKQ; | ||
6672 | #endif | ||
6586 | struct backcmd in; | 6673 | struct backcmd in; |
6587 | int i; | 6674 | int i; |
6588 | char buf[128]; | 6675 | char buf[128]; |
@@ -6597,9 +6684,15 @@ expbackq(union node *cmd, int flag) | |||
6597 | INT_OFF; | 6684 | INT_OFF; |
6598 | startloc = expdest - (char *)stackblock(); | 6685 | startloc = expdest - (char *)stackblock(); |
6599 | pushstackmark(&smark, startloc); | 6686 | pushstackmark(&smark, startloc); |
6600 | evalbackcmd(cmd, &in); | 6687 | evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl)); |
6601 | popstackmark(&smark); | 6688 | popstackmark(&smark); |
6602 | 6689 | ||
6690 | if (ctl != CTLBACKQ) { | ||
6691 | sprintf(buf, DEV_FD_PREFIX"%d", in.fd); | ||
6692 | strtodest(buf, BASESYNTAX); | ||
6693 | goto done; | ||
6694 | } | ||
6695 | |||
6603 | p = in.buf; | 6696 | p = in.buf; |
6604 | i = in.nleft; | 6697 | i = in.nleft; |
6605 | if (i == 0) | 6698 | if (i == 0) |
@@ -6621,6 +6714,7 @@ expbackq(union node *cmd, int flag) | |||
6621 | close(in.fd); | 6714 | close(in.fd); |
6622 | back_exitstatus = waitforjob(in.jp); | 6715 | back_exitstatus = waitforjob(in.jp); |
6623 | } | 6716 | } |
6717 | done: | ||
6624 | INT_ON; | 6718 | INT_ON; |
6625 | 6719 | ||
6626 | /* Eat all trailing newlines */ | 6720 | /* Eat all trailing newlines */ |
@@ -6708,6 +6802,10 @@ argstr(char *p, int flag) | |||
6708 | CTLESC, | 6802 | CTLESC, |
6709 | CTLVAR, | 6803 | CTLVAR, |
6710 | CTLBACKQ, | 6804 | CTLBACKQ, |
6805 | #if BASH_PROCESS_SUBST | ||
6806 | CTLTOPROC, | ||
6807 | CTLFROMPROC, | ||
6808 | #endif | ||
6711 | #if ENABLE_FEATURE_SH_MATH | 6809 | #if ENABLE_FEATURE_SH_MATH |
6712 | CTLARI, | 6810 | CTLARI, |
6713 | CTLENDARI, | 6811 | CTLENDARI, |
@@ -6807,8 +6905,12 @@ argstr(char *p, int flag) | |||
6807 | p = evalvar(p, flag | inquotes); | 6905 | p = evalvar(p, flag | inquotes); |
6808 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); | 6906 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); |
6809 | goto start; | 6907 | goto start; |
6908 | #if BASH_PROCESS_SUBST | ||
6909 | case CTLTOPROC: | ||
6910 | case CTLFROMPROC: | ||
6911 | #endif | ||
6810 | case CTLBACKQ: | 6912 | case CTLBACKQ: |
6811 | expbackq(argbackq->n, flag | inquotes); | 6913 | expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c)); |
6812 | goto start; | 6914 | goto start; |
6813 | #if ENABLE_FEATURE_SH_MATH | 6915 | #if ENABLE_FEATURE_SH_MATH |
6814 | case CTLARI: | 6916 | case CTLARI: |
@@ -12198,8 +12300,9 @@ realeofmark(const char *eofmark) | |||
12198 | #define CHECKEND() {goto checkend; checkend_return:;} | 12300 | #define CHECKEND() {goto checkend; checkend_return:;} |
12199 | #define PARSEREDIR() {goto parseredir; parseredir_return:;} | 12301 | #define PARSEREDIR() {goto parseredir; parseredir_return:;} |
12200 | #define PARSESUB() {goto parsesub; parsesub_return:;} | 12302 | #define PARSESUB() {goto parsesub; parsesub_return:;} |
12201 | #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} | 12303 | #define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;} |
12202 | #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} | 12304 | #define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;} |
12305 | #define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} | ||
12203 | #define PARSEARITH() {goto parsearith; parsearith_return:;} | 12306 | #define PARSEARITH() {goto parsearith; parsearith_return:;} |
12204 | static int | 12307 | static int |
12205 | readtoken1(int c, int syntax, char *eofmark, int striptabs) | 12308 | readtoken1(int c, int syntax, char *eofmark, int striptabs) |
@@ -12210,7 +12313,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12210 | size_t len; | 12313 | size_t len; |
12211 | struct nodelist *bqlist; | 12314 | struct nodelist *bqlist; |
12212 | smallint quotef; | 12315 | smallint quotef; |
12213 | smallint oldstyle; | 12316 | smallint style; |
12317 | enum { OLD, NEW, PSUB }; | ||
12318 | #define oldstyle (style == OLD) | ||
12214 | smallint pssyntax; /* we are expanding a prompt string */ | 12319 | smallint pssyntax; /* we are expanding a prompt string */ |
12215 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) | 12320 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) |
12216 | /* syntax stack */ | 12321 | /* syntax stack */ |
@@ -12392,6 +12497,15 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12392 | pungetc(); | 12497 | pungetc(); |
12393 | } | 12498 | } |
12394 | #endif | 12499 | #endif |
12500 | #if BASH_PROCESS_SUBST | ||
12501 | if (c == '<' || c == '>') { | ||
12502 | if (pgetc() == '(') { | ||
12503 | PARSEPROCSUB(); | ||
12504 | break; | ||
12505 | } | ||
12506 | pungetc(); | ||
12507 | } | ||
12508 | #endif | ||
12395 | goto endword; /* exit outer loop */ | 12509 | goto endword; /* exit outer loop */ |
12396 | } | 12510 | } |
12397 | IF_ASH_ALIAS(if (c != PEOA)) | 12511 | IF_ASH_ALIAS(if (c != PEOA)) |
@@ -12876,9 +12990,18 @@ parsebackq: { | |||
12876 | memcpy(out, str, savelen); | 12990 | memcpy(out, str, savelen); |
12877 | STADJUST(savelen, out); | 12991 | STADJUST(savelen, out); |
12878 | } | 12992 | } |
12879 | USTPUTC(CTLBACKQ, out); | 12993 | #if BASH_PROCESS_SUBST |
12994 | if (style == PSUB) | ||
12995 | USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out); | ||
12996 | else | ||
12997 | #endif | ||
12998 | USTPUTC(CTLBACKQ, out); | ||
12880 | if (oldstyle) | 12999 | if (oldstyle) |
12881 | goto parsebackq_oldreturn; | 13000 | goto parsebackq_oldreturn; |
13001 | #if BASH_PROCESS_SUBST | ||
13002 | else if (style == PSUB) | ||
13003 | goto parsebackq_psreturn; | ||
13004 | #endif | ||
12882 | goto parsebackq_newreturn; | 13005 | goto parsebackq_newreturn; |
12883 | } | 13006 | } |
12884 | 13007 | ||
@@ -13330,6 +13453,9 @@ cmdloop(int top) | |||
13330 | if (doing_jobctl) | 13453 | if (doing_jobctl) |
13331 | showjobs(SHOW_CHANGED|SHOW_STDERR); | 13454 | showjobs(SHOW_CHANGED|SHOW_STDERR); |
13332 | #endif | 13455 | #endif |
13456 | #if BASH_PROCESS_SUBST | ||
13457 | unwindredir(NULL); | ||
13458 | #endif | ||
13333 | inter = 0; | 13459 | inter = 0; |
13334 | if (iflag && top) { | 13460 | if (iflag && top) { |
13335 | inter++; | 13461 | inter++; |
diff --git a/shell/ash_test/ash-psubst/bash_procsub.right b/shell/ash_test/ash-psubst/bash_procsub.right new file mode 100644 index 000000000..aa16a96be --- /dev/null +++ b/shell/ash_test/ash-psubst/bash_procsub.right | |||
@@ -0,0 +1,9 @@ | |||
1 | hello 1 | ||
2 | hello 2 | ||
3 | hello 3 | ||
4 | <(echo "hello 0") | ||
5 | hello 4 | ||
6 | HI THERE | ||
7 | hello error | ||
8 | hello error | ||
9 | hello stderr | ||
diff --git a/shell/ash_test/ash-psubst/bash_procsub.tests b/shell/ash_test/ash-psubst/bash_procsub.tests new file mode 100755 index 000000000..63b836782 --- /dev/null +++ b/shell/ash_test/ash-psubst/bash_procsub.tests | |||
@@ -0,0 +1,33 @@ | |||
1 | # simplest case | ||
2 | cat <(echo "hello 1") | ||
3 | |||
4 | # can have more than one | ||
5 | cat <(echo "hello 2") <(echo "hello 3") | ||
6 | |||
7 | # doesn't work in quotes | ||
8 | echo "<(echo \"hello 0\")" | ||
9 | |||
10 | # process substitution can be nested inside command substitution | ||
11 | echo $(cat <(echo "hello 4")) | ||
12 | |||
13 | # example from http://wiki.bash-hackers.org/syntax/expansion/proc_subst | ||
14 | # process substitutions can be passed to a function as parameters or | ||
15 | # variables | ||
16 | f() { | ||
17 | cat "$1" >"$x" | ||
18 | } | ||
19 | x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there') | ||
20 | |||
21 | # process substitution can be combined with redirection on exec | ||
22 | rm -f err | ||
23 | # save stderr | ||
24 | exec 4>&2 | ||
25 | # copy stderr to a file | ||
26 | exec 2> >(tee err) | ||
27 | echo "hello error" >&2 | ||
28 | sync | ||
29 | # restore stderr | ||
30 | exec 2>&4 | ||
31 | cat err | ||
32 | rm -f err | ||
33 | echo "hello stderr" >&2 | ||