aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-10 00:20:58 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-10 00:20:58 +0000
commitc96865f4458f357df41eeea73d456e15755b51f4 (patch)
tree2ff83e9aa8062a05aea94c9b576af5f5c8870b55
parente05f9286a943eb74bdcdead86c016e6c8cc5c082 (diff)
downloadbusybox-w32-c96865f4458f357df41eeea73d456e15755b51f4.tar.gz
busybox-w32-c96865f4458f357df41eeea73d456e15755b51f4.tar.bz2
busybox-w32-c96865f4458f357df41eeea73d456e15755b51f4.zip
hush: readability improvements.
fix some more obscure bugs. a new redir4.tests is known to fail.
-rw-r--r--shell/hush.c216
-rw-r--r--shell/hush_test/hush-misc/redir1.right2
-rwxr-xr-xshell/hush_test/hush-misc/redir1.tests6
-rw-r--r--shell/hush_test/hush-misc/redir4.right25
-rwxr-xr-xshell/hush_test/hush-misc/redir4.tests80
5 files changed, 232 insertions, 97 deletions
diff --git a/shell/hush.c b/shell/hush.c
index ac2410c48..21590adfb 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -341,7 +341,11 @@ typedef enum redir_type {
341 REDIRECT_HEREDOC = 4, 341 REDIRECT_HEREDOC = 4,
342 REDIRECT_IO = 5, 342 REDIRECT_IO = 5,
343 REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */ 343 REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */
344 REDIRFD_CLOSE = -3, 344
345 REDIRFD_CLOSE = -3,
346 REDIRFD_SYNTAX_ERR = -2,
347 REDIRFD_TO_FILE = -1, /* otherwise, rd_fd if redirected to rd_dup */
348
345 HEREDOC_SKIPTABS = 1, 349 HEREDOC_SKIPTABS = 1,
346 HEREDOC_QUOTED = 2, 350 HEREDOC_QUOTED = 2,
347} redir_type; 351} redir_type;
@@ -2427,6 +2431,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
2427 2431
2428 for (redir = prog->redirects; redir; redir = redir->next) { 2432 for (redir = prog->redirects; redir; redir = redir->next) {
2429 if (redir->rd_type == REDIRECT_HEREDOC2) { 2433 if (redir->rd_type == REDIRECT_HEREDOC2) {
2434 /* rd_fd<<HERE case */
2430 if (squirrel && redir->rd_fd < 3) { 2435 if (squirrel && redir->rd_fd < 3) {
2431 squirrel[redir->rd_fd] = dup(redir->rd_fd); 2436 squirrel[redir->rd_fd] = dup(redir->rd_fd);
2432 } 2437 }
@@ -2438,15 +2443,16 @@ static int setup_redirects(struct command *prog, int squirrel[])
2438 continue; 2443 continue;
2439 } 2444 }
2440 2445
2441 if (redir->rd_dup == -1) { 2446 if (redir->rd_dup == REDIRFD_TO_FILE) {
2447 /* rd_fd<*>file case (<*> is <,>,>>,<>) */
2442 char *p; 2448 char *p;
2443 if (redir->rd_filename == NULL) { 2449 if (redir->rd_filename == NULL) {
2444 /* Something went wrong in the parse. 2450 /* Something went wrong in the parse.
2445 * Pretend it didn't happen */ 2451 * Pretend it didn't happen */
2452 bb_error_msg("bug in redirect parse");
2446 continue; 2453 continue;
2447 } 2454 }
2448 mode = redir_table[redir->rd_type].mode; 2455 mode = redir_table[redir->rd_type].mode;
2449//TODO: check redir for names like '\\'
2450 p = expand_string_to_string(redir->rd_filename); 2456 p = expand_string_to_string(redir->rd_filename);
2451 openfd = open_or_warn(p, mode); 2457 openfd = open_or_warn(p, mode);
2452 free(p); 2458 free(p);
@@ -2457,6 +2463,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
2457 return 1; 2463 return 1;
2458 } 2464 }
2459 } else { 2465 } else {
2466 /* rd_fd<*>rd_dup or rd_fd<*>- cases */
2460 openfd = redir->rd_dup; 2467 openfd = redir->rd_dup;
2461 } 2468 }
2462 2469
@@ -2469,7 +2476,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
2469 close(redir->rd_fd); 2476 close(redir->rd_fd);
2470 } else { 2477 } else {
2471 xdup2(openfd, redir->rd_fd); 2478 xdup2(openfd, redir->rd_fd);
2472 if (redir->rd_dup == -1) 2479 if (redir->rd_dup == REDIRFD_TO_FILE)
2473 close(openfd); 2480 close(openfd);
2474 } 2481 }
2475 } 2482 }
@@ -3963,17 +3970,6 @@ static int done_word(o_string *word, struct parse_context *ctx)
3963 debug_printf_parse("done_word return 0: true null, ignored\n"); 3970 debug_printf_parse("done_word return 0: true null, ignored\n");
3964 return 0; 3971 return 0;
3965 } 3972 }
3966 /* If this word wasn't an assignment, next ones definitely
3967 * can't be assignments. Even if they look like ones. */
3968 if (word->o_assignment != DEFINITELY_ASSIGNMENT
3969 && word->o_assignment != WORD_IS_KEYWORD
3970 ) {
3971 word->o_assignment = NOT_ASSIGNMENT;
3972 } else {
3973 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
3974 command->assignment_cnt++;
3975 word->o_assignment = MAYBE_ASSIGNMENT;
3976 }
3977 3973
3978 if (ctx->pending_redirect) { 3974 if (ctx->pending_redirect) {
3979 /* We do not glob in e.g. >*.tmp case. bash seems to glob here 3975 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
@@ -3989,29 +3985,47 @@ static int done_word(o_string *word, struct parse_context *ctx)
3989 * the expansion would result in one word." 3985 * the expansion would result in one word."
3990 */ 3986 */
3991 ctx->pending_redirect->rd_filename = xstrdup(word->data); 3987 ctx->pending_redirect->rd_filename = xstrdup(word->data);
3988 /* Cater for >\file case:
3989 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
3990 * Same with heredocs:
3991 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
3992 */
3993 unbackslash(ctx->pending_redirect->rd_filename);
3994 /* Is it <<"HEREDOC"? */
3992 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC 3995 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC
3993 && word->o_quoted 3996 && word->o_quoted
3994 ) { 3997 ) {
3995 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED; 3998 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
3996 } 3999 }
3997 word->o_assignment = NOT_ASSIGNMENT;
3998 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); 4000 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
3999 } else { 4001 } else {
4000 /* "{ echo foo; } echo bar" - bad */ 4002 /* If this word wasn't an assignment, next ones definitely
4001 /* NB: bash allows e.g.: 4003 * can't be assignments. Even if they look like ones. */
4002 * if true; then { echo foo; } fi 4004 if (word->o_assignment != DEFINITELY_ASSIGNMENT
4003 * while if false; then false; fi do break; done 4005 && word->o_assignment != WORD_IS_KEYWORD
4004 * and disallows: 4006 ) {
4005 * while if false; then false; fi; do; break; done 4007 word->o_assignment = NOT_ASSIGNMENT;
4006 * TODO? */ 4008 } else {
4009 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
4010 command->assignment_cnt++;
4011 word->o_assignment = MAYBE_ASSIGNMENT;
4012 }
4013
4007 if (command->group) { 4014 if (command->group) {
4015 /* "{ echo foo; } echo bar" - bad */
4016 /* NB: bash allows e.g.:
4017 * if true; then { echo foo; } fi
4018 * while if false; then false; fi do break; done
4019 * and disallows:
4020 * while if false; then false; fi; do; break; done
4021 * TODO? */
4008 syntax_error_at(word->data); 4022 syntax_error_at(word->data);
4009 debug_printf_parse("done_word return 1: syntax error, " 4023 debug_printf_parse("done_word return 1: syntax error, "
4010 "groups and arglists don't mix\n"); 4024 "groups and arglists don't mix\n");
4011 return 1; 4025 return 1;
4012 } 4026 }
4013#if HAS_KEYWORDS 4027#if HAS_KEYWORDS
4014#if ENABLE_HUSH_CASE 4028# if ENABLE_HUSH_CASE
4015 if (ctx->ctx_dsemicolon 4029 if (ctx->ctx_dsemicolon
4016 && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */ 4030 && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4017 ) { 4031 ) {
@@ -4019,12 +4033,12 @@ static int done_word(o_string *word, struct parse_context *ctx)
4019 /* ctx->ctx_res_w = RES_MATCH; */ 4033 /* ctx->ctx_res_w = RES_MATCH; */
4020 ctx->ctx_dsemicolon = 0; 4034 ctx->ctx_dsemicolon = 0;
4021 } else 4035 } else
4022#endif 4036# endif
4023 if (!command->argv /* if it's the first word... */ 4037 if (!command->argv /* if it's the first word... */
4024#if ENABLE_HUSH_LOOPS 4038# if ENABLE_HUSH_LOOPS
4025 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ 4039 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
4026 && ctx->ctx_res_w != RES_IN 4040 && ctx->ctx_res_w != RES_IN
4027#endif 4041# endif
4028 ) { 4042 ) {
4029 debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); 4043 debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
4030 if (reserved_word(word, ctx)) { 4044 if (reserved_word(word, ctx)) {
@@ -4090,20 +4104,23 @@ static int done_word(o_string *word, struct parse_context *ctx)
4090 4104
4091/* Peek ahead in the input to find out if we have a "&n" construct, 4105/* Peek ahead in the input to find out if we have a "&n" construct,
4092 * as in "2>&1", that represents duplicating a file descriptor. 4106 * as in "2>&1", that represents duplicating a file descriptor.
4093 * Return: REDIRFD_CLOSE (-3) if >&- "close fd" construct is seen, 4107 * Return:
4094 * -2 (syntax error), -1 if no & was seen, or the number found. 4108 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
4109 * REDIRFD_SYNTAX_ERR if syntax error,
4110 * REDIRFD_TO_FILE if no & was seen,
4111 * or the number found.
4095 */ 4112 */
4096#if BB_MMU 4113#if BB_MMU
4097#define redirect_dup_num(as_string, input) \ 4114#define parse_redir_right_fd(as_string, input) \
4098 redirect_dup_num(input) 4115 parse_redir_right_fd(input)
4099#endif 4116#endif
4100static int redirect_dup_num(o_string *as_string, struct in_str *input) 4117static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4101{ 4118{
4102 int ch, d, ok; 4119 int ch, d, ok;
4103 4120
4104 ch = i_peek(input); 4121 ch = i_peek(input);
4105 if (ch != '&') 4122 if (ch != '&')
4106 return -1; 4123 return REDIRFD_TO_FILE;
4107 4124
4108 ch = i_getch(input); /* get the & */ 4125 ch = i_getch(input); /* get the & */
4109 nommu_addchr(as_string, ch); 4126 nommu_addchr(as_string, ch);
@@ -4127,10 +4144,10 @@ static int redirect_dup_num(o_string *as_string, struct in_str *input)
4127//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2) 4144//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
4128 4145
4129 bb_error_msg("ambiguous redirect"); 4146 bb_error_msg("ambiguous redirect");
4130 return -2; 4147 return REDIRFD_SYNTAX_ERR;
4131} 4148}
4132 4149
4133/* Return code is 0 normally, 1 if a syntax error is detected 4150/* Return code is 0 normal, 1 if a syntax error is detected
4134 */ 4151 */
4135static int parse_redirect(struct parse_context *ctx, 4152static int parse_redirect(struct parse_context *ctx,
4136 int fd, 4153 int fd,
@@ -4142,12 +4159,12 @@ static int parse_redirect(struct parse_context *ctx,
4142 struct redir_struct **redirp; 4159 struct redir_struct **redirp;
4143 int dup_num; 4160 int dup_num;
4144 4161
4145 dup_num = -1; 4162 dup_num = REDIRFD_TO_FILE;
4146 if (style != REDIRECT_HEREDOC) { 4163 if (style != REDIRECT_HEREDOC) {
4147 /* Check for a '2>&1' type redirect */ 4164 /* Check for a '>&1' type redirect */
4148 dup_num = redirect_dup_num(&ctx->as_string, input); 4165 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4149 if (dup_num == -2) 4166 if (dup_num == REDIRFD_SYNTAX_ERR)
4150 return 1; /* syntax error */ 4167 return 1;
4151 } else { 4168 } else {
4152 int ch = i_peek(input); 4169 int ch = i_peek(input);
4153 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ 4170 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
@@ -4158,7 +4175,7 @@ static int parse_redirect(struct parse_context *ctx,
4158 } 4175 }
4159 } 4176 }
4160 4177
4161 if (style == REDIRECT_OVERWRITE && dup_num == -1) { 4178 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4162 int ch = i_peek(input); 4179 int ch = i_peek(input);
4163 if (ch == '|') { 4180 if (ch == '|') {
4164 /* >|FILE redirect ("clobbering" >). 4181 /* >|FILE redirect ("clobbering" >).
@@ -4185,7 +4202,7 @@ static int parse_redirect(struct parse_context *ctx,
4185 redir_table[style].descrip); 4202 redir_table[style].descrip);
4186 4203
4187 redir->rd_dup = dup_num; 4204 redir->rd_dup = dup_num;
4188 if (style != REDIRECT_HEREDOC && dup_num != -1) { 4205 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
4189 /* Erik had a check here that the file descriptor in question 4206 /* Erik had a check here that the file descriptor in question
4190 * is legit; I postpone that to "run time" 4207 * is legit; I postpone that to "run time"
4191 * A "-" representation of "close me" shows up as a -3 here */ 4208 * A "-" representation of "close me" shows up as a -3 here */
@@ -4862,9 +4879,6 @@ static int parse_stream_dquoted(o_string *as_string,
4862 * only when followed by one of the following characters: 4879 * only when followed by one of the following characters:
4863 * $, `, ", \, or <newline>. A double quote may be quoted 4880 * $, `, ", \, or <newline>. A double quote may be quoted
4864 * within double quotes by preceding it with a backslash. 4881 * within double quotes by preceding it with a backslash.
4865 * If enabled, history expansion will be performed unless
4866 * an ! appearing in double quotes is escaped using
4867 * a backslash. The backslash preceding the ! is not removed."
4868 */ 4882 */
4869 if (strchr("$`\"\\", next) != NULL) { 4883 if (strchr("$`\"\\", next) != NULL) {
4870 o_addqchr(dest, i_getch(input)); 4884 o_addqchr(dest, i_getch(input));
@@ -5081,17 +5095,71 @@ static struct pipe *parse_stream(char **pstring,
5081 if (is_ifs) 5095 if (is_ifs)
5082 continue; 5096 continue;
5083 5097
5084 if (dest.o_assignment == MAYBE_ASSIGNMENT) {
5085 /* ch is a special char and thus this word
5086 * cannot be an assignment */
5087 dest.o_assignment = NOT_ASSIGNMENT;
5088 }
5089
5090 next = '\0'; 5098 next = '\0';
5091 if (ch != '\n') { 5099 if (ch != '\n') {
5092 next = i_peek(input); 5100 next = i_peek(input);
5093 } 5101 }
5094 5102
5103 /* Catch <, > before deciding whether this word is
5104 * an assignment. a=1 2>z b=2: b=2 is still assignment */
5105 switch (ch) {
5106 case '>':
5107 redir_fd = redirect_opt_num(&dest);
5108 if (done_word(&dest, &ctx)) {
5109 goto parse_error;
5110 }
5111 redir_style = REDIRECT_OVERWRITE;
5112 if (next == '>') {
5113 redir_style = REDIRECT_APPEND;
5114 ch = i_getch(input);
5115 nommu_addchr(&ctx.as_string, ch);
5116 }
5117#if 0
5118 else if (next == '(') {
5119 syntax_error(">(process) not supported");
5120 goto parse_error;
5121 }
5122#endif
5123 if (parse_redirect(&ctx, redir_fd, redir_style, input))
5124 goto parse_error;
5125 continue; /* back to top of while (1) */
5126 case '<':
5127 redir_fd = redirect_opt_num(&dest);
5128 if (done_word(&dest, &ctx)) {
5129 goto parse_error;
5130 }
5131 redir_style = REDIRECT_INPUT;
5132 if (next == '<') {
5133 redir_style = REDIRECT_HEREDOC;
5134 heredoc_cnt++;
5135 debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
5136 ch = i_getch(input);
5137 nommu_addchr(&ctx.as_string, ch);
5138 } else if (next == '>') {
5139 redir_style = REDIRECT_IO;
5140 ch = i_getch(input);
5141 nommu_addchr(&ctx.as_string, ch);
5142 }
5143#if 0
5144 else if (next == '(') {
5145 syntax_error("<(process) not supported");
5146 goto parse_error;
5147 }
5148#endif
5149 if (parse_redirect(&ctx, redir_fd, redir_style, input))
5150 goto parse_error;
5151 continue; /* back to top of while (1) */
5152 }
5153
5154 if (dest.o_assignment == MAYBE_ASSIGNMENT
5155 /* check that we are not in word in "a=1 2>word b=1": */
5156 && !ctx.pending_redirect
5157 ) {
5158 /* ch is a special char and thus this word
5159 * cannot be an assignment */
5160 dest.o_assignment = NOT_ASSIGNMENT;
5161 }
5162
5095 switch (ch) { 5163 switch (ch) {
5096 case '#': 5164 case '#':
5097 if (dest.length == 0) { 5165 if (dest.length == 0) {
@@ -5171,52 +5239,6 @@ static struct pipe *parse_stream(char **pstring,
5171 break; 5239 break;
5172 } 5240 }
5173#endif 5241#endif
5174 case '>':
5175 redir_fd = redirect_opt_num(&dest);
5176 if (done_word(&dest, &ctx)) {
5177 goto parse_error;
5178 }
5179 redir_style = REDIRECT_OVERWRITE;
5180 if (next == '>') {
5181 redir_style = REDIRECT_APPEND;
5182 ch = i_getch(input);
5183 nommu_addchr(&ctx.as_string, ch);
5184 }
5185#if 0
5186 else if (next == '(') {
5187 syntax_error(">(process) not supported");
5188 goto parse_error;
5189 }
5190#endif
5191 if (parse_redirect(&ctx, redir_fd, redir_style, input))
5192 goto parse_error;
5193 break;
5194 case '<':
5195 redir_fd = redirect_opt_num(&dest);
5196 if (done_word(&dest, &ctx)) {
5197 goto parse_error;
5198 }
5199 redir_style = REDIRECT_INPUT;
5200 if (next == '<') {
5201 redir_style = REDIRECT_HEREDOC;
5202 heredoc_cnt++;
5203 debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
5204 ch = i_getch(input);
5205 nommu_addchr(&ctx.as_string, ch);
5206 } else if (next == '>') {
5207 redir_style = REDIRECT_IO;
5208 ch = i_getch(input);
5209 nommu_addchr(&ctx.as_string, ch);
5210 }
5211#if 0
5212 else if (next == '(') {
5213 syntax_error("<(process) not supported");
5214 goto parse_error;
5215 }
5216#endif
5217 if (parse_redirect(&ctx, redir_fd, redir_style, input))
5218 goto parse_error;
5219 break;
5220 case ';': 5242 case ';':
5221#if ENABLE_HUSH_CASE 5243#if ENABLE_HUSH_CASE
5222 case_semi: 5244 case_semi:
diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/hush_test/hush-misc/redir1.right
index ac90b4a0a..15515d1af 100644
--- a/shell/hush_test/hush-misc/redir1.right
+++ b/shell/hush_test/hush-misc/redir1.right
@@ -1,3 +1,5 @@
1Test 0: var:ok
2File created:ok
1Test 1: var:ok 3Test 1: var:ok
2File created:ok 4File created:ok
3Test 2: var:ok 5Test 2: var:ok
diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/hush_test/hush-misc/redir1.tests
index 7e204514c..70e9e17f0 100755
--- a/shell/hush_test/hush-misc/redir1.tests
+++ b/shell/hush_test/hush-misc/redir1.tests
@@ -1,5 +1,11 @@
1rm shell_test_$$ 2>/dev/null 1rm shell_test_$$ 2>/dev/null
2var=bad 2var=bad
3>shell_test_$$ var=ok
4echo "Test 0: var:$var"
5test -f shell_test_$$ && echo "File created:ok"
6
7rm shell_test_$$ 2>/dev/null
8var=bad
3var=ok >shell_test_$$ 9var=ok >shell_test_$$
4echo "Test 1: var:$var" 10echo "Test 1: var:$var"
5test -f shell_test_$$ && echo "File created:ok" 11test -f shell_test_$$ && echo "File created:ok"
diff --git a/shell/hush_test/hush-misc/redir4.right b/shell/hush_test/hush-misc/redir4.right
new file mode 100644
index 000000000..ada6c2d85
--- /dev/null
+++ b/shell/hush_test/hush-misc/redir4.right
@@ -0,0 +1,25 @@
1shell_test
2\shell_test
3\shell_test
4\shell_test
5Here1
6Ok1
7Here2
8Ok2
9Here3
10Ok3
11Here4
12Ok4
13How with variable refs
14shell_test_1
15\shell_test_1
16\shell_test_1
17\shell_test_1
18Here1
19Ok1
20Here2
21Ok2
22Here3
23Ok3
24Here4
25Ok4
diff --git a/shell/hush_test/hush-misc/redir4.tests b/shell/hush_test/hush-misc/redir4.tests
new file mode 100755
index 000000000..ac2a44166
--- /dev/null
+++ b/shell/hush_test/hush-misc/redir4.tests
@@ -0,0 +1,80 @@
1rm *shell_test* 2>/dev/null
2
3>\shell_test
4echo *shell_test*
5rm *shell_test*
6
7>\\shell_test
8echo *shell_test*
9rm *shell_test*
10
11>"\shell_test"
12echo *shell_test*
13rm *shell_test*
14
15>"\\shell_test"
16echo *shell_test*
17rm *shell_test*
18
19
20cat <<\shell_test
21Here1
22shell_test
23echo Ok1
24
25cat <<\\shell_test
26Here2
27\shell_test
28echo Ok2
29
30cat <<"\shell_test"
31Here3
32\shell_test
33echo Ok3
34
35cat <<"\\shell_test"
36Here4
37\shell_test
38echo Ok4
39
40
41echo How with variable refs
42i=1
43
44
45>\shell_test_$i
46echo *shell_test*
47rm *shell_test*
48
49>\\shell_test_$i
50echo *shell_test*
51rm *shell_test*
52
53>"\shell_test_$i"
54echo *shell_test*
55rm *shell_test*
56
57>"\\shell_test_$i"
58echo *shell_test*
59rm *shell_test*
60
61
62cat <<\shell_test_$i
63Here1
64shell_test_$i
65echo Ok1
66
67cat <<\\shell_test_$i
68Here2
69\shell_test_$i
70echo Ok2
71
72cat <<"\shell_test_$i"
73Here3
74\shell_test_$i
75echo Ok3
76
77cat <<"\\shell_test_$i"
78Here4
79\shell_test_$i
80echo Ok4