mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 12:23:15 +00:00
Shell: Allow assignment-prefixed commands to run builtins
`env` is not capable of running shell builtins, so make a simple builtin in its place.
This commit is contained in:
parent
e2336d9de5
commit
fca5a34ad3
|
@ -1876,6 +1876,62 @@ ErrorOr<int> Shell::builtin_read(Main::Arguments arguments)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ErrorOr<int> Shell::builtin_run_with_env(Main::Arguments arguments)
|
||||
{
|
||||
Vector<DeprecatedString> environment_variables;
|
||||
Vector<StringView> command_and_arguments;
|
||||
|
||||
Core::ArgsParser parser;
|
||||
parser.add_option(environment_variables, "Environment variables to set", "env", 'e', "NAME=VALUE");
|
||||
parser.add_positional_argument(command_and_arguments, "Command and arguments to run", "command", Core::ArgsParser::Required::Yes);
|
||||
parser.set_stop_on_first_non_option(true);
|
||||
|
||||
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
|
||||
return 1;
|
||||
|
||||
if (command_and_arguments.is_empty()) {
|
||||
warnln("run_with_env: No command to run");
|
||||
return 1;
|
||||
}
|
||||
|
||||
AST::Command command;
|
||||
TRY(command.argv.try_ensure_capacity(command_and_arguments.size()));
|
||||
for (auto& arg : command_and_arguments)
|
||||
command.argv.append(TRY(String::from_utf8(arg)));
|
||||
|
||||
auto commands = TRY(expand_aliases({ move(command) }));
|
||||
|
||||
HashMap<DeprecatedString, Optional<DeprecatedString>> old_environment_entries;
|
||||
for (auto& variable : environment_variables) {
|
||||
auto parts = variable.split_limit('=', 2, SplitBehavior::KeepEmpty);
|
||||
if (parts.size() != 2) {
|
||||
warnln("run_with_env: Invalid environment variable: '{}'", variable);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DeprecatedString name = parts[0];
|
||||
old_environment_entries.set(name, getenv(name.characters()) ?: Optional<DeprecatedString> {});
|
||||
|
||||
DeprecatedString value = parts[1];
|
||||
setenv(name.characters(), value.characters(), 1);
|
||||
}
|
||||
|
||||
int exit_code = 0;
|
||||
for (auto& job : run_commands(commands)) {
|
||||
block_on_job(job);
|
||||
exit_code = job->exit_code();
|
||||
}
|
||||
|
||||
for (auto& entry : old_environment_entries) {
|
||||
if (entry.value.has_value())
|
||||
setenv(entry.key.characters(), entry.value->characters(), 1);
|
||||
else
|
||||
unsetenv(entry.key.characters());
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
bool Shell::has_builtin(StringView name) const
|
||||
{
|
||||
if (name == ":"sv)
|
||||
|
|
|
@ -1849,22 +1849,33 @@ ErrorOr<RefPtr<AST::Node>> Parser::parse_simple_command()
|
|||
while (peek().type == Token::Type::AssignmentWord) {
|
||||
definitions.append(peek().value);
|
||||
|
||||
if (!nodes.is_empty()) {
|
||||
nodes.append(
|
||||
make_ref_counted<AST::BarewordLiteral>(
|
||||
peek().position.value_or(empty_position()),
|
||||
consume().value));
|
||||
} else {
|
||||
// env (assignments) (command)
|
||||
if (nodes.is_empty()) {
|
||||
// run_with_env -e*(assignments) -- (command)
|
||||
nodes.append(make_ref_counted<AST::BarewordLiteral>(
|
||||
empty_position(),
|
||||
"env"_short_string));
|
||||
|
||||
nodes.append(
|
||||
make_ref_counted<AST::BarewordLiteral>(
|
||||
peek().position.value_or(empty_position()),
|
||||
consume().value));
|
||||
TRY("run_with_env"_string)));
|
||||
}
|
||||
|
||||
auto position = peek().position.value_or(empty_position());
|
||||
nodes.append(make_ref_counted<AST::ImmediateExpression>(
|
||||
position,
|
||||
AST::NameWithPosition {
|
||||
TRY("reexpand"_string),
|
||||
position,
|
||||
},
|
||||
Vector<NonnullRefPtr<AST::Node>> {
|
||||
make_ref_counted<AST::StringLiteral>(
|
||||
position,
|
||||
TRY(String::formatted("-e{}", consume().value)),
|
||||
AST::StringLiteral::EnclosureType::DoubleQuotes),
|
||||
},
|
||||
Optional<AST::Position> {}));
|
||||
}
|
||||
|
||||
if (!definitions.is_empty()) {
|
||||
nodes.append(make_ref_counted<AST::BarewordLiteral>(
|
||||
empty_position(),
|
||||
"--"_short_string));
|
||||
}
|
||||
|
||||
// WORD or io_redirect: IO_NUMBER or io_file
|
||||
|
@ -1879,13 +1890,24 @@ ErrorOr<RefPtr<AST::Node>> Parser::parse_simple_command()
|
|||
auto split_offset = equal_offset.value_or(definition.bytes().size());
|
||||
auto name = make_ref_counted<AST::BarewordLiteral>(
|
||||
empty_position(),
|
||||
definition.substring_from_byte_offset_with_shared_superstring(0, split_offset).release_value_but_fixme_should_propagate_errors());
|
||||
TRY(definition.substring_from_byte_offset_with_shared_superstring(0, split_offset)));
|
||||
|
||||
auto value = make_ref_counted<AST::BarewordLiteral>(
|
||||
empty_position(),
|
||||
definition.substring_from_byte_offset_with_shared_superstring(equal_offset.map([](auto x) { return x + 1; }).value_or(definition.bytes().size())).release_value_but_fixme_should_propagate_errors());
|
||||
auto position = peek().position.value_or(empty_position());
|
||||
auto expanded_value = make_ref_counted<AST::ImmediateExpression>(
|
||||
position,
|
||||
AST::NameWithPosition {
|
||||
TRY("reexpand"_string),
|
||||
position,
|
||||
},
|
||||
Vector<NonnullRefPtr<AST::Node>> {
|
||||
make_ref_counted<AST::StringLiteral>(
|
||||
position,
|
||||
TRY(definition.substring_from_byte_offset_with_shared_superstring(split_offset + 1)),
|
||||
AST::StringLiteral::EnclosureType::DoubleQuotes),
|
||||
},
|
||||
Optional<AST::Position> {});
|
||||
|
||||
variables.append({ move(name), move(value) });
|
||||
variables.append({ move(name), move(expanded_value) });
|
||||
}
|
||||
|
||||
return make_ref_counted<AST::VariableDeclarations>(empty_position(), move(variables));
|
||||
|
|
|
@ -23,40 +23,41 @@
|
|||
#include <LibMain/Main.h>
|
||||
#include <termios.h>
|
||||
|
||||
#define ENUMERATE_SHELL_BUILTINS() \
|
||||
__ENUMERATE_SHELL_BUILTIN(alias) \
|
||||
__ENUMERATE_SHELL_BUILTIN(where) \
|
||||
__ENUMERATE_SHELL_BUILTIN(cd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(cdh) \
|
||||
__ENUMERATE_SHELL_BUILTIN(pwd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(type) \
|
||||
__ENUMERATE_SHELL_BUILTIN(exec) \
|
||||
__ENUMERATE_SHELL_BUILTIN(exit) \
|
||||
__ENUMERATE_SHELL_BUILTIN(export) \
|
||||
__ENUMERATE_SHELL_BUILTIN(glob) \
|
||||
__ENUMERATE_SHELL_BUILTIN(unalias) \
|
||||
__ENUMERATE_SHELL_BUILTIN(unset) \
|
||||
__ENUMERATE_SHELL_BUILTIN(history) \
|
||||
__ENUMERATE_SHELL_BUILTIN(umask) \
|
||||
__ENUMERATE_SHELL_BUILTIN(not ) \
|
||||
__ENUMERATE_SHELL_BUILTIN(dirs) \
|
||||
__ENUMERATE_SHELL_BUILTIN(pushd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(popd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(setopt) \
|
||||
__ENUMERATE_SHELL_BUILTIN(shift) \
|
||||
__ENUMERATE_SHELL_BUILTIN(source) \
|
||||
__ENUMERATE_SHELL_BUILTIN(time) \
|
||||
__ENUMERATE_SHELL_BUILTIN(jobs) \
|
||||
__ENUMERATE_SHELL_BUILTIN(disown) \
|
||||
__ENUMERATE_SHELL_BUILTIN(fg) \
|
||||
__ENUMERATE_SHELL_BUILTIN(bg) \
|
||||
__ENUMERATE_SHELL_BUILTIN(wait) \
|
||||
__ENUMERATE_SHELL_BUILTIN(dump) \
|
||||
__ENUMERATE_SHELL_BUILTIN(kill) \
|
||||
__ENUMERATE_SHELL_BUILTIN(noop) \
|
||||
__ENUMERATE_SHELL_BUILTIN(break) \
|
||||
__ENUMERATE_SHELL_BUILTIN(continue) \
|
||||
__ENUMERATE_SHELL_BUILTIN(read) \
|
||||
#define ENUMERATE_SHELL_BUILTINS() \
|
||||
__ENUMERATE_SHELL_BUILTIN(alias) \
|
||||
__ENUMERATE_SHELL_BUILTIN(where) \
|
||||
__ENUMERATE_SHELL_BUILTIN(cd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(cdh) \
|
||||
__ENUMERATE_SHELL_BUILTIN(pwd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(type) \
|
||||
__ENUMERATE_SHELL_BUILTIN(exec) \
|
||||
__ENUMERATE_SHELL_BUILTIN(exit) \
|
||||
__ENUMERATE_SHELL_BUILTIN(export) \
|
||||
__ENUMERATE_SHELL_BUILTIN(glob) \
|
||||
__ENUMERATE_SHELL_BUILTIN(unalias) \
|
||||
__ENUMERATE_SHELL_BUILTIN(unset) \
|
||||
__ENUMERATE_SHELL_BUILTIN(history) \
|
||||
__ENUMERATE_SHELL_BUILTIN(umask) \
|
||||
__ENUMERATE_SHELL_BUILTIN(not ) \
|
||||
__ENUMERATE_SHELL_BUILTIN(dirs) \
|
||||
__ENUMERATE_SHELL_BUILTIN(pushd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(popd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(setopt) \
|
||||
__ENUMERATE_SHELL_BUILTIN(shift) \
|
||||
__ENUMERATE_SHELL_BUILTIN(source) \
|
||||
__ENUMERATE_SHELL_BUILTIN(time) \
|
||||
__ENUMERATE_SHELL_BUILTIN(jobs) \
|
||||
__ENUMERATE_SHELL_BUILTIN(disown) \
|
||||
__ENUMERATE_SHELL_BUILTIN(fg) \
|
||||
__ENUMERATE_SHELL_BUILTIN(bg) \
|
||||
__ENUMERATE_SHELL_BUILTIN(wait) \
|
||||
__ENUMERATE_SHELL_BUILTIN(dump) \
|
||||
__ENUMERATE_SHELL_BUILTIN(kill) \
|
||||
__ENUMERATE_SHELL_BUILTIN(noop) \
|
||||
__ENUMERATE_SHELL_BUILTIN(break) \
|
||||
__ENUMERATE_SHELL_BUILTIN(continue) \
|
||||
__ENUMERATE_SHELL_BUILTIN(read) \
|
||||
__ENUMERATE_SHELL_BUILTIN(run_with_env) \
|
||||
__ENUMERATE_SHELL_BUILTIN(argsparser_parse)
|
||||
|
||||
#define ENUMERATE_SHELL_OPTIONS() \
|
||||
|
|
Loading…
Reference in a new issue