瀏覽代碼

Shell: Allow assignment-prefixed commands to run builtins

`env` is not capable of running shell builtins, so make a simple
builtin in its place.
Ali Mohammad Pur 2 年之前
父節點
當前提交
fca5a34ad3
共有 3 個文件被更改,包括 131 次插入52 次删除
  1. 56 0
      Userland/Shell/Builtin.cpp
  2. 40 18
      Userland/Shell/PosixParser.cpp
  3. 35 34
      Userland/Shell/Shell.h

+ 56 - 0
Userland/Shell/Builtin.cpp

@@ -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)

+ 40 - 18
Userland/Shell/PosixParser.cpp

@@ -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));

+ 35 - 34
Userland/Shell/Shell.h

@@ -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()                                                                                    \