diff --git a/examples/shell_command/example_cmd.sh b/examples/shell_command/example_cmd.sh new file mode 100755 index 000000000..ff250de46 --- /dev/null +++ b/examples/shell_command/example_cmd.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# This script can be called by a PyDMShellCommand widget, +# allowing it to make use of command chaining and other shell features. +echo "Hello World!" && echo "Hello Again!" diff --git a/examples/shell_command/shell_command_full_shell.ui b/examples/shell_command/shell_command_full_shell.ui new file mode 100644 index 000000000..b5e6b2442 --- /dev/null +++ b/examples/shell_command/shell_command_full_shell.ui @@ -0,0 +1,256 @@ + + + Form + + + + 0 + 0 + 458 + 386 + + + + Form + + + + + + + 0 + 0 + + + + <html><head/><body><p>The PyDMShellCommand button with run your command through a full shell if you enable the 'runCommandsInFullShell' option. This allows you to use some additional features such as shell syntax ('|', '&amp;', ';', etc), environment variables ($VAR), glob expansion ('*', '?', etc), and some other features.</p></body></html> + + + true + + + + + + + + + + + + + runCmdsInFullShell Option Enabled + + + false + + + true + + + + + + + + + false + + + true + + + Are you sure you want to proceed? + + + + + + true + + + true + + + false + + + + + + + echo First; echo Second + + + + false + + + + + + + + + true + + + + + + + You can run through a Bash shell (without needing to enable any options) by specifying "bash -c" at the start of your command. + + + true + + + + + + + + + + + + + Using "-c bash" + + + false + + + true + + + + + + + + + false + + + Are you sure you want to proceed? + + + + + + true + + + true + + + false + + + + + + + bash -c "echo 'Hello One'; echo 'Hello two'" + + + + false + + + + + + + + + false + + + + + + + You can also call a shell script. For this button to work correctly, run pydm from dir 'examples/shell_command' so it can find the script file. + + + true + + + + + + + + + + Calling external script + + + false + + + true + + + + + + + + + false + + + Are you sure you want to proceed? + + + + + + true + + + true + + + false + + + + Print "Hello, World!" to terminal + Print current working directory to terminal + + + + + ./example_cmd.sh + + + + false + + + + + + + + + false + + + + + + + + PyDMShellCommand + QPushButton +
pydm.widgets.shell_command
+
+
+ + +
diff --git a/pydm/widgets/shell_command.py b/pydm/widgets/shell_command.py index 553ed4220..ac1280411 100644 --- a/pydm/widgets/shell_command.py +++ b/pydm/widgets/shell_command.py @@ -68,6 +68,9 @@ def __init__( self.process = None self._show_icon = True self._redirect_output = False + # shell allows for more options such as command chaining ("cmd1;cmd2", "cmd1 && cmd2", etc ...), + # use of environment variables, glob expansion ('ls *.txt'), etc... + self._run_commands_in_full_shell = False self._password_protected = False self._password = "" @@ -203,6 +206,29 @@ def showConfirmDialog(self, value: bool) -> None: if self._show_confirm_dialog != value: self._show_confirm_dialog = value + @Property(bool) + def runCommandsInFullShell(self) -> bool: + """ + Whether or not to run cmds with Popen's option for running them through a shell subprocess. + + Returns + ------- + bool + """ + return self._run_commands_in_full_shell + + @runCommandsInFullShell.setter + def runCommandsInFullShell(self, value: bool) -> None: + """ + Whether or not to run cmds with Popen's option for running them through a shell subprocess. + + Parameters + ---------- + value : bool + """ + if self._run_commands_in_full_shell != value: + self._run_commands_in_full_shell = value + @Property(str) def confirmMessage(self) -> str: """ @@ -603,6 +629,9 @@ def execute_command(self, command: str) -> None: if (self.process is None or self.process.poll() is not None) or self._allow_multiple: cmd = os.path.expanduser(os.path.expandvars(command)) args = shlex.split(cmd, posix="win" not in sys.platform) + # when shell enabled, Popen should take the cmds as a single string (not list) + if self._run_commands_in_full_shell: + args = cmd try: logger.debug("Launching process: %s", repr(args)) stdout = subprocess.PIPE @@ -614,7 +643,10 @@ def execute_command(self, command: str) -> None: if self._redirect_output: stdout = None - self.process = subprocess.Popen(args, stdout=stdout, stderr=subprocess.PIPE, env=env_var) + self.process = subprocess.Popen( + args, stdout=stdout, stderr=subprocess.PIPE, env=env_var, shell=self._run_commands_in_full_shell + ) + except Exception as exc: self.show_warning_icon() logger.error("Error in shell command: %s", exc)