1

I'm writing a CLI app in Rust and need to run commands differently depending on if it is being run in CMD or Powershell. There's this SO question that gives a command "(dir 2>&1 *`|echo CMD);&<# rem #>echo POWERSHELL" to run to output "CMD" or "POWERSHELL", but I can't run that as a Command in Rust.

This code returns the following error: Error { kind: NotFound, message: "program not found" }

let output = Command::new("(dir 2>&1 *`|echo CMD);&<# rem #>echo POWERSHELL")
        .output();

The problem is that some CMD/PowerShell commands don't work from Rust's std::process::Command and need to be called directly with CMD or PowerShell, but without that command, I can't seem to figure out how to detect the shell.

Idk if there's another way with Rust to determine what Windows shell is being ran, but any consistent method would also work for what I have in mind.

Update: To clarify my use case, I'm writing a program to set environment variables temporarily and run a given command provided by the user.

# sets $Env:ENV1=VALUE, then runs the command "dir" with args ["env:"]
temporary-env -e ENV1=VALUE1 dir -- env:

This example doesn't needs to determine the shell type, but other commands like dir have different syntax for args and parameters based on which shell is used, so I want to replicate that behavior based on which shell.

10
  • 2
    Why don't just run the command directly from your specified shell cmd /c command or powershell -c command? No need to care about it anymore Commented Jul 30, 2022 at 1:49
  • The CLI app I'm using lets you pass in commands and run them, so I want the commands to function as if they were in that shell. I want the stdio to function the same way it would when run in that shell. Commented Jul 30, 2022 at 2:18
  • cmd or powershell are commands, just pass the whole thing into anything that expects a command Commented Jul 30, 2022 at 2:41
  • 4
    @Mason I get what you are trying to do, I think, but I think Rust is the wrong language for it. As soon as you enter a Rust program, you loose all information about what context it is being run in. There is no reliable way of doing what you want to achieve here. You could look it up heuristically. Either way, I think it will be rather flaky and error prone, not just finding it out, but all the rest as well. For example, running an interactive app like vim through your program. Commented Jul 30, 2022 at 9:15
  • 3
    @Mason one of the design philosophies of rust crates is that you should be able to write them once and then compile them everywhere, including platforms that don't even exist yet. To prevent having a big jungle of platform specific libraries, and to make it easy to port rust to new targets. And for the most part, this works great. But the entire Rust standard library is created with this philosophy in mind, and therefore your usecase is hard to realize. Commented Jul 31, 2022 at 7:19

2 Answers 2

2

You can execute commands that will only work in one shell (but not crash in the other) to determine which shell you are using. E. g.:

echo %PATH%

Will output the content of your PATH environment variable in CMD, but just the string %PATH% in PowerShell. Vice versa:

echo $env:Path

Will output the content of your PATH environment variable in PowerShell, but just the string $env:Path in CMD. So, depending on the output, you can make a guess on which shell you are using.

This may currently work between CMD and PowerShell, but as phuclv already stated, this is not a stable design for a software. What if the user chooses a third - yet unknown - shell?

Sign up to request clarification or add additional context in comments.

Comments

1

Regardless of the platform, whether Windows, Linux, BSD, macOS... you never need to determine the current shell type. You decide the shell you want to use and pass a command in that syntax. If you need to run a PowerShell command just run

powershell "temporary-env -e ENV1=VALUE1 dir -- env:"
powershell "$env:MYVAR = 'value'; somecommand"

powershell -c dir will always be run as a PowerShell command so the syntax and output will always be consistent. Similarly bash -c dir or zsh -c dir also runs a command in that shell

You can't detect the shell environment reliably because programs aren't always run from a terminal, and the parent can strip environment information when executing the child process. The only thing you can do is to walk the process tree to see if any ancestor is a known shell, but of course it won't always work


Update: To clarify my use case, I'm writing a program to set environment variables temporarily and run a given command provided by the user.

You still need to ask the user about the shell they want to use. Even in *nix there are lots of shells like sh, fish, pwsh, nushell, zsh, csh, ash, dash, pysh... All of them have different syntax and the command may likely fail or produce unexpected output if running in the wrong shell.
How do you know that the user is using pysh? What if the user wants to run a zsh command while in fish? The best thing you can do is to assume a default shell if the user doesn't tell you that, like /bin/sh on *nix and cmd on Windows (or powershell if you don't intend to support Windows older than Vista)

2 Comments

I think there's a disconnect in what I'm looking for and what my question is coming off as. I'm trying to write a program in rust (temporary-env) that takes in parameters and runs a provided command and any extra parameters. In rust, the only way to do this is with Command, which can't certain commands such as echo without running it through a shell (powershell or cmd). I don't want hard code which shell, since users may use my program with cmd or powershell, so using powershell -c will force them to use different syntax in cmd.
@Mason that's not possible. For example your program can be executed from another GUI program that's not a shell so there's no shell information at all. Even if running from a shell it's easy to erase shell information. The user is expected to specify which shell they want to use

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.