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.
CodePudding user response:
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 bywsl.exe
wsl.exe --cd ~ 'for f in $(find -xdev -type l); do readlink $f; done'
That would be the equivalent of runningcd ~; 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 asfor
.
"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