mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:52:43 +00:00 
			
		
		
		
	Shell: Parse and correctly evaluate 'command &' and 'command &&'
This commit adds the InBackground and ShortCircuitOnFailure attributes to commands, which respectively cause the command to be run in the background, and abort the command chain with exit status != 0.
This commit is contained in:
		
							parent
							
								
									143be7234f
								
							
						
					
					
						commit
						c23c354779
					
				
					 3 changed files with 70 additions and 5 deletions
				
			
		|  | @ -49,11 +49,11 @@ void Parser::commit_subcommand() | ||||||
|     m_subcommands.append({ move(m_tokens), move(m_redirections), {} }); |     m_subcommands.append({ move(m_tokens), move(m_redirections), {} }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Parser::commit_command() | void Parser::commit_command(Attributes attributes) | ||||||
| { | { | ||||||
|     if (m_subcommands.is_empty()) |     if (m_subcommands.is_empty()) | ||||||
|         return; |         return; | ||||||
|     m_commands.append({ move(m_subcommands) }); |     m_commands.append({ move(m_subcommands), attributes }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Parser::do_pipe() | void Parser::do_pipe() | ||||||
|  | @ -109,6 +109,31 @@ Vector<Command> Parser::parse() | ||||||
|                 commit_command(); |                 commit_command(); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |             if (ch == '&') { | ||||||
|  |                 commit_token(Token::Special); | ||||||
|  | 
 | ||||||
|  |                 if (i + 1 >= m_input.length()) { | ||||||
|  |                 in_background:; | ||||||
|  |                     // Nothing interesting past this token, commit with InBackground
 | ||||||
|  |                     commit_subcommand(); | ||||||
|  |                     commit_command(Attributes::InBackground); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 ch = m_input.characters()[++i]; | ||||||
|  |                 ++m_position; | ||||||
|  | 
 | ||||||
|  |                 if (ch == '&') { | ||||||
|  |                     // This is '&&', commit with ShortCircuit
 | ||||||
|  |                     commit_subcommand(); | ||||||
|  |                     commit_command(Attributes::ShortCircuitOnFailure); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 --i; | ||||||
|  |                 --m_position; | ||||||
|  |                 goto in_background; | ||||||
|  |             } | ||||||
|             if (ch == '|') { |             if (ch == '|') { | ||||||
|                 commit_token(Token::Special); |                 commit_token(Token::Special); | ||||||
|                 if (m_tokens.is_empty()) { |                 if (m_tokens.is_empty()) { | ||||||
|  |  | ||||||
|  | @ -45,6 +45,12 @@ struct Token { | ||||||
|     Type type; |     Type type; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | enum Attributes { | ||||||
|  |     None = 0x0, | ||||||
|  |     ShortCircuitOnFailure = 0x1, | ||||||
|  |     InBackground = 0x2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| struct Redirection { | struct Redirection { | ||||||
|     enum Type { |     enum Type { | ||||||
|         Pipe, |         Pipe, | ||||||
|  | @ -71,6 +77,7 @@ struct Subcommand { | ||||||
| 
 | 
 | ||||||
| struct Command { | struct Command { | ||||||
|     Vector<Subcommand> subcommands; |     Vector<Subcommand> subcommands; | ||||||
|  |     Attributes attributes; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Parser { | class Parser { | ||||||
|  | @ -89,7 +96,7 @@ private: | ||||||
|     }; |     }; | ||||||
|     void commit_token(Token::Type, AllowEmptyToken = AllowEmptyToken::No); |     void commit_token(Token::Type, AllowEmptyToken = AllowEmptyToken::No); | ||||||
|     void commit_subcommand(); |     void commit_subcommand(); | ||||||
|     void commit_command(); |     void commit_command(Attributes = None); | ||||||
|     void do_pipe(); |     void do_pipe(); | ||||||
|     void begin_redirect_read(int fd); |     void begin_redirect_read(int fd); | ||||||
|     void begin_redirect_write(int fd); |     void begin_redirect_write(int fd); | ||||||
|  |  | ||||||
|  | @ -1052,7 +1052,6 @@ IterationDecision Shell::wait_for_pid(const Shell::SpawnedProcess& process, bool | ||||||
|         if (WEXITSTATUS(wstatus) != 0) |         if (WEXITSTATUS(wstatus) != 0) | ||||||
|             dbg() << "Shell: " << process.name << ":" << process.pid << " exited with status " << WEXITSTATUS(wstatus); |             dbg() << "Shell: " << process.name << ":" << process.pid << " exited with status " << WEXITSTATUS(wstatus); | ||||||
| 
 | 
 | ||||||
|         if (is_first_command_in_chain) |  | ||||||
|         return_value = WEXITSTATUS(wstatus); |         return_value = WEXITSTATUS(wstatus); | ||||||
| 
 | 
 | ||||||
|         if (job) { |         if (job) { | ||||||
|  | @ -1128,6 +1127,7 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             dbgprintf("\n"); |             dbgprintf("\n"); | ||||||
|             for (auto& redirecton : command.subcommands[i].redirections) { |             for (auto& redirecton : command.subcommands[i].redirections) { | ||||||
|                 for (size_t j = 0; j < i; ++j) |                 for (size_t j = 0; j < i; ++j) | ||||||
|  | @ -1151,6 +1151,13 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         if (auto attributes = command.attributes) { | ||||||
|  |             dbgprintf("\n "); | ||||||
|  |             if (attributes & Attributes::InBackground) | ||||||
|  |                 dbgprintf("InBackground "); | ||||||
|  |             if (attributes & Attributes::ShortCircuitOnFailure) | ||||||
|  |                 dbgprintf("ShortCircuitOnFailure "); | ||||||
|  |         } | ||||||
|         dbgprintf("\n"); |         dbgprintf("\n"); | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  | @ -1159,8 +1166,20 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | ||||||
|     tcgetattr(0, &trm); |     tcgetattr(0, &trm); | ||||||
| 
 | 
 | ||||||
|     int return_value = 0; |     int return_value = 0; | ||||||
|  |     bool fail_short_circuits = false; | ||||||
| 
 | 
 | ||||||
|     for (auto& command : commands) { |     for (auto& command : commands) { | ||||||
|  |         if (fail_short_circuits) { | ||||||
|  |             if (command.attributes & Attributes::ShortCircuitOnFailure) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             // Do not fail any command after this one, as we've reached the end of a short-circuit chain,
 | ||||||
|  |             // e.g. foo && bar && baz ; foobar
 | ||||||
|  |             //                    ^ we reached this command.
 | ||||||
|  |             fail_short_circuits = false; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if (command.subcommands.is_empty()) |         if (command.subcommands.is_empty()) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
|  | @ -1306,6 +1325,14 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | ||||||
|             dbgprintf("  %d (%s)\n", child.pid, child.name.characters()); |             dbgprintf("  %d (%s)\n", child.pid, child.name.characters()); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  |         if (command.attributes & Attributes::InBackground) { | ||||||
|  |             // Set the jobs as running in background and continue without waiting.
 | ||||||
|  |             for (auto& child : children) | ||||||
|  |                 const_cast<Job*>(jobs.get(child.pid).value())->set_running_in_background(true); | ||||||
|  | 
 | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         for (size_t i = 0; i < children.size(); ++i) { |         for (size_t i = 0; i < children.size(); ++i) { | ||||||
|             auto& child = children[i]; |             auto& child = children[i]; | ||||||
|             dbg() << "Now waiting for " << child.name << " (" << child.pid << ")"; |             dbg() << "Now waiting for " << child.name << " (" << child.pid << ")"; | ||||||
|  | @ -1314,6 +1341,12 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd) | ||||||
|                     break; |                     break; | ||||||
|             } while (errno == EINTR); |             } while (errno == EINTR); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if (command.attributes & Attributes::ShortCircuitOnFailure) { | ||||||
|  |             if (return_value != 0) { | ||||||
|  |                 fail_short_circuits = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     last_return_code = return_value; |     last_return_code = return_value; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 AnotherTest
						AnotherTest