1

I want to put together a PowerShell script that invokes bash/wsl and runs the following code on the working directory in bash:

for f in $(find -type l);do cp --remove-destination $(readlink $f) $f;done;

All I can seem to do is get wsl to change directories:

param (
    [Parameter(Mandatory=$true,Position=0)]
    [String]$Folder
)

wsl.exe --cd $Folder

I've tried:

wsl -c 'for f in $(find -type l);do cp --remove-destination $(readlink $f) $f;done;'

wsl.exe 'for f in $(find -type l);do cp --remove-destination $(readlink $f) $f;done;'

Also, for some reason, I can't get any additional commands to work after I've called wsl.exe the first time. For instance, the following doesn't work:

param (
    [Parameter(Mandatory=$true,Position=0)]
    [String]$Folder
)

wsl.exe --cd $Folder
wsl.exe echo "Hello World" # Never executes

I'm pretty inexperienced with linux and Ubuntu for that matter, so I have a feeling I'm missing something incredibly simple - thus this post.

How can I execute a bash/wsl2 command from Windows Powershell 7?

Any help greatly appreciated.

5
  • I never have used powershell or wsl, so I can't give a definitive answer. However, AFIK, wsl does not have a -c option (therefore wsl -c probably won't work), and you must pass it an executable (for is not an executable). What happens if you do a wsl /usr/bin/uname or, for instance, a wsl bash -c echo hello world? Do you get at least an error message? Commented Apr 25, 2022 at 11:23
  • wsl bash -c echo hello world outputs nothing in both Powershell and Command Prompt. Commented Apr 25, 2022 at 11:31
  • And uname the same? Commented Apr 25, 2022 at 11:38
  • If you don't get any output at all, I wonder whether you are really running the "correct" wsl. What happens if you specify the absolute path to WSL explicitly? Commented Apr 25, 2022 at 11:43
  • @user1934428 If I type wsl into windows command prompt, or a powershell terminal, I get a bash prompt. Doesn't matter if I specify the full path to wsl or just wsl. I'm positive I'm running the correct wsl. Commented Apr 25, 2022 at 12:45

1 Answer 1

2

I have a feeling I'm missing something incredibly simple

Not really. In my experience, calling one scripting language from another is rarely what-I-would-call "simple" ;-).

That's a good attempt to figure it out from the help, but unfortunately there's just not enough detail in the wsl --help output to figure it out from that.

As noted in the help, though, there are a few constructs available for running commands in a WSL instance using the wsl.exe command.

  • wsl <commandline>: Runs the command line as an argument to the default shell.
  • wsl -e <command>: Runs the command in place of the shell

Note that, for the sake of safety, I'm going to replace your command-lines with more benign versions ;-).

for f in $(find -xdev -type l); do echo $f "----" $(readlink $f); done

Side note: As someone once pointed out to me when I used find with for -- Why is looping over find's output bad practice?, so it would really be better off as find -xdev -type l | xargs -I % sh -c "echo -n % '---- '; readlink %", but we're going to leave it in a for loop to demonstrate part of the problem here.

The following two tries fail because ...

  • wsl --cd ~ -c 'for f in $(find -xdev -type l); do readlink $f; done'

    -c is not a flag that is understood by wsl.exe

  • wsl.exe --cd ~ 'for f in $(find -xdev -type l); do readlink $f; done'

    That would be the equivalent of running cd ~; for f in $(find -xdev -type l); do readlink $f; done, which won't work either, of course.

    Actually, in retrospect, this might work for you. It failed for me because my default shell is Fish, but wsl does seem to attempt to run the default shell with -c for whatever command-line is passed in.

    It may have failed for you because you weren't setting the directory (via --cd) on the same command-line before calling it.

  • wsl.exe --cd ~ -e 'for f in $(find -xdev -type l); do readlink $f; done'

    And, while you didn't mention trying this, this particular one won't work either, since -e needs to be a single "command", but that's a full commandline with shell builtins such as for.

"Aaargggh!", you've been saying, right? Catch 22? You need -c to get to the shell, but the wsl command can't pass it.

So, we use:

wsl --cd ~ -e sh -c 'for f in $(find -xdev -type l); do echo $f "----" $(readlink $f); done'

That:

  • Changes to the home directory (you can replace with the $FOLDER variable from PowerShell, of course)
  • Executes the sh shell (you could also use Bash (or any other shell or command) if you need any of its constructs)
  • Passes the commandline via -c to the shell. This is a fairly normal argument for most shells, POSIX or otherwise.

Note (from experience) that quoting rules between PowerShell and WSL/sh can get fairly "unruly". I find that as the example gets more complicated, it's often better to put the shell commands in a script inside WSL, which you then execute from PowerShell. For example, something like:

wsl --cd ~ -e sh -c "~/.local/bin/myscript.sh"
--cd note

Using two separate wsl commands, like in your --cd example:

wsl.exe --cd $Folder
wsl.exe echo "Hello World" # Never executes

Assuming you were executing that via a script, the first line:

  • Changes to the specified directory
  • Runs the default shell, so you are then in an interactive session

If you then exit the shell (Ctrl+D or exit), then you should see the output from the second command.

You can also see this interactively if you run it from PowerShell via:

wsl --cd ~ ; wsl echo Hello
Sign up to request clarification or add additional context in comments.

5 Comments

Wow! Thank you so much for going into such detail and explaining everything step by step. I am overjoyed to have a solution. Question: If I wrap my script into a .sh file, how do I make sure that it runs only on the current directory (or a directory of my choosing)? In your last example: wsl --cd ~ -e sh -c "~/.local/bin/myscript.sh" Would myscript.sh automatically then operate on ~ (or $Folder if I replace the ~)? I want to make sure that I don't accidentally run the script the wrong way and target unintended directories. Thank you so much again.
You're welcome! And yes, the directory that you change to with --cd would then be the current-working-directory for the script. That said, test well, of course. That's why I switched to benign commands. You could also pass the $FOLDER in as an argument to the script, then cd from inside the script. Either way, if possible, the script should test to see if it's a directory that fits any "pattern" you can think of that would either say "this is an okay directory to run in" or not. Also make sure that variables are quoted properly to make sure that spaces-in-names isn't a problem.
@Jay Another thought - Some folks have their script generate the commands that will be run, so that you can review them and then run them separately. For instance, echo rm file1 >> paranoid_script.sh. Then, if something goes wrong (with the cd, for instance), you will spot it in the resulting paranoid_script.sh before running it.
Taking all of this in! Do you know if I pass in the Powershell $Folder argument as-is (Windows Format, E.g. C:\Users\Username\Desktop\TargetFolder) if WSL/Bash will automatically convert it to the corresponding *NIX folder structure? Or should I implement a function that transforms the input folder from Win to Linux format, such as: /mnt/c/Users/Username/Desktop/TargetFolder? After testing a couple folders it seems to work without converting the path, but I want to make sure.
@Jay Should be fine without converting. Originally, the --cd argument always took a Windows path. Now (at least under Windows 11, and I believe under Windows 10 21H2), it can also take an absolute Linux path (e.g. /) or ~ or (in Preview versions only) a relative path under ~ (e.g. ~/.config).

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.