I need to create ONE integrated script that sets some environment variables, downloads a file using wget and runs it.
The challenge is that it needs to be the SAME script that can run on both Windows PowerShell and also bash / shell.
This is the shell script:
#!/bin/bash
# download a script
wget http://www.example.org/my.script -O my.script
# set a couple of environment variables
export script_source=http://www.example.org
export some_value=floob
# now execute the downloaded script
bash ./my.script
This is the same thing in PowerShell:
wget http://www.example.org/my.script -O my.script.ps1
$env:script_source="http://www.example.org"
$env:some_value="floob"
PowerShell -File ./my.script.ps1
So I wonder if somehow these two scripts can be merged and run successfully on either platform?
I've been trying to find a way to put them in the same script and get bash and PowerShell.exe to ignore errors but have had no success doing so.
Any guesses?
Luckily, on Linux if you put a shebang at the start of the script and the path to the interpreter we want to use. Then all that is left is marking the script as executable. Once these items are in place, we can execute the PowerShell script directly from Bash.
SH file is very similar to the batch file of the Windows operating system and can be run in the Linux-based operating system. It is also possible to run . SH file in Windows 10 using Windows Subsystem for Linux.
PowerShell is not just a shell; it is a complete scripting environment. PowerShell invokes lightweight commands called cmdlets at run-time via automated scripts or APIs.
Following up on Jeff Hykin's answer, I have found that the first line, while it is happy in bash, produces this output in PowerShell. Note that it is still fully functional, just noisy.
true : The term 'true' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\jp\scratch\envar.ps1:4 char:1
+ true --% ; : '
+ ~~~~
+ CategoryInfo : ObjectNotFound: (true:String) [], CommandNotFoundException
hello from powershell
I am experimenting with changing the first lines from:
true --% ; : '
<#'
to:
echo --% > /dev/null ; : ' | out-null
<#'
In very limited testing this seems to be working in bash and powershell. For reference, I am "sourcing" the scripts not "calling" them, e.g. . env.ps1
in bash and . ./env.ps1
in powershell.
(and also thank you j-p-hutchins for fixing the error with true
)
Save this as your_thing.ps1
for it to run as powershell on Windows and run as shell on all other operating systems.
#!/usr/bin/env sh
echo --% >/dev/null;: ' | out-null
<#'
#
# sh part
#
echo "hello from bash/dash/zsh"
echo "do whatver you want just dont use #> directly"
echo "e.g. do #""> or something similar"
# end bash part
exit #>
#
# powershell part
#
echo "hello from powershell"
echo "you literally don't have to escape anything here"
<#
but as-is they would cause problems in bash/shell languages. We need to use a string like "<#"
for bash, but we need it to NOT be a string in powershell.--%
lets write single quote without starting a string, e.g. echo --% ' blah '
will print out ' blah '
. This is great because in shell/bash the single quotes do start a string, e.g. echo --% ' blah '
will print out blah
echo
command<#
, but powershell the same code finishes the echo command then starts a multi-line comment>/dev/null
to bash so that it doesn't print out --%
every time, and we add | out-null
so that powershell doesn't print out >/dev/null;: '
every time.The syntax highlighting tells the story more visually
All the green stuff is ignored by powershell (comments)
The gray --%
is special
The | out-null
is special
The white parts are just string-arguments without quotes
(even the single quote is equivlent to "'"
)
The <#
is the start of a multi-line comment
For bash its totally different.
Lime green + underline are the commands.
The --%
isn't special, its just an argument
But the ;
is special
The purple is output-redirection
Then :
is just the standard "do nothing" shell command
Then the '
starts a string argument that ends on the next line
Almost almost none. Powershell legitimately has no downside. The Bash caveats are easy to fix, and are exceedingly rare
"#>"
to "#"">"
' blah #> '
to ' blah #''> '
.#>
and for some reason you CANNOT change that comment (this is what I mean by exceedingly rare), you can actually just use #>
, you just have to add re-add those first two lines (eg true --%
etc) right after your #>
comment#
to remove parts of a string (I bet most don't even know this is a bash feature). Example code below
https://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION
var1=">blah"
echo ${var1#>}
# ^ removes the > from var1
To fix this one, well there are alternative ways of removeing chars from the begining of a string, use them instead.
It is possible; I don't know how compatible this is, but PowerShell treats strings as text and they end up on screen, Bash treats them as commands and tries to run them, and both support the same function definition syntax. So, put a function name in quotes and only Bash will run it, put "exit" in quotes and only Bash will exit. Then write PowerShell code after.
NB. this works because the syntax in both shells overlaps, and your script is simple - run commands and deal with variables. If you try to use more advanced script (if/then, for, switch, case, etc.) for either language, the other one will probably complain.
Save this as dual.ps1
so PowerShell is happy with it, chmod +x dual.ps1
so Bash will run it
#!/bin/bash
function DoBashThings {
wget http://www.example.org/my.script -O my.script
# set a couple of environment variables
export script_source=http://www.example.org
export some_value=floob
# now execute the downloaded script
bash ./my.script
}
"DoBashThings" # This runs the bash script, in PS it's just a string
"exit" # This quits the bash version, in PS it's just a string
# PowerShell code here
# --------------------
Invoke-WebRequest "http://www.example.org/my.script.ps1" -OutFile my.script.ps1
$env:script_source="http://www.example.org"
$env:some_value="floob"
PowerShell -File ./my.script.ps1
then
./dual.ps1
on either system.
Edit: You can include more complex code by commenting the code blocks with a distinct prefix, then having each language filter out its own code and eval
it (usual security caveats apply with eval), e.g. with this approach (incorporating suggestion from Harry Johnston ):
#!/bin/bash
#posh $num = 200
#posh if (150 -lt $num) {
#posh write-host "PowerShell here"
#posh }
#bash thing="xyz"
#bash if [ "$thing" = "xyz" ]
#bash then
#bash echo "Bash here"
#bash fi
function RunBashStuff {
eval "$(grep '^#bash' $0 | sed -e 's/^#bash //')"
}
"RunBashStuff"
"exit"
((Get-Content $MyInvocation.MyCommand.Source) -match '^#posh' -replace '^#posh ') -join "`n" | Invoke-Expression
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With