是否可以编写一个在bash/shell和PowerShell中运行的脚本?

Duk*_*gal 4 linux windows bash shell powershell

我需要创建一个集成脚本来设置一些环境变量,使用wget下载文件并运行它.

挑战在于它需要是可以在Windows PowerShell和bash/shell上运行的SAME脚本.

这是shell脚本:

#!/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
Run Code Online (Sandbox Code Playgroud)

这与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
Run Code Online (Sandbox Code Playgroud)

所以我想知道这两个脚本是否可以合并并在任一平台上成功运行?

我一直在试图找到一种方法将它们放在同一个脚本中,并让bash和PowerShell.exe忽略错误,但没有成功.

任何猜测?

Jef*_*kin 12

虽然另一个答案很棒
(谢谢TesselttingHecklerHarry Johnston

\n

(也感谢jp-huchins修复了错误true

\n

我们实际上可以做得更好

\n
    \n
  1. 使用更多 shell(例如适用于 Ubuntu 的 dash)
  2. \n
  3. 在未来的情况下不太可能破裂
  4. \n
  5. 无需浪费处理时间重新读取/评估脚本
  6. \n
  7. 在令人困惑的语法上浪费更少的字符/行
    (我们可以只用 41 个字符和 3 行)
  8. \n
  9. 甚至保持语法高亮功能
  10. \n
\n

复制粘贴代码

\n

将其保存为your_thing.ps1在 Windows 上作为 powershell 运行,并在所有其他操作系统上作为 shell 运行。

\n
#!/usr/bin/env sh\necho --% >/dev/null;: \' | out-null\n<#\'\n\n\n#\n# sh part\n#\necho "hello from bash/dash/zsh"\necho "do whatver you want just dont use #\xef\xbc\x9e directly"\necho "e.g. do #""> or something similar"\n# end bash part\n\nexit #>\n\n\n#\n# powershell part\n#\necho "hello from powershell"\necho "you literally don\'t have to escape anything here"\n
Run Code Online (Sandbox Code Playgroud)\n

如何?(其实很简单)

\n
    \n
  • 我们希望在 powershell 中启动多行注释,而不会在 bash/shell 中引起错误。
  • \n
  • Powershell 具有多行注释<#,但它们会在 bash/shell 语言中引起问题。我们需要使用类似于 bash 的字符串"<#",但我们需要它不是 powershell 中的字符串。
  • \n
  • Powershell 有一个停止解析参数,--%可以在不开始字符串的情况下写入单引号,例如
    echo --% \' blah \'将打印出\' blah \'。这很棒,因为在 shell/bash 中单引号确实会启动一个字符串,例如
    echo --% \' blah \'将打印出来blah
  • \n
  • 我们需要一个命令才能使用 powershell 的 stop-parsing-args,幸运的是 powershell 和 bash 都有一个echo命令
  • \n
  • 因此,在 bash 中我们可以用 回显一个字符串<#,但在 powershell 中相同的代码完成回显命令然后启动多行注释
  • \n
  • 最后我们添加>/dev/null到 bash 中,以便它不会--%每次都打印出来,并且我们添加到| out-nullpowershell 中,以便它不会>/dev/null;: \'每次都打印出来。
  • \n
\n

语法高亮更直观地讲述故事

\n

Powershell 突出显示

\n

所有绿色的东西都会被powershell忽略(注释)
\n灰色--%是特殊的
\n| out-null是特殊的
\n白色部分只是不带引号的字符串参数\n
(即使单引号相当于"\'"
\n这<#是一个多引号的开始行注释

\n

powershell 突出显示

\n

Bash 突出显示

\n

对于 bash 来说,情况完全不同。
\n石灰绿+下划线是命令。
\n--%这并不特殊,它只是一个参数
\n但是;很特殊
\n紫色是输出重定向
\n然后:只是标准的“不执行任何操作”shell 命令
\n然后\'启动一个在下一行结束的字符串参数

\n

bash 突出显示

\n

注意事项?

\n

几乎没有。Powershell 确实没有缺点。Bash 警告很容易修复,而且极其罕见

\n
    \n
  1. 如果您需要在 bash 字符串中使用#>,则需要以某种方式对其进行转义。
    \n更改"#>""#"">"
    或从\' blah #> \'更改为\' blah #\'\'> \'
  2. \n
  3. 如果您有评论#>并且由于某种原因您无法更改该评论(这就是我所说的极其罕见的情况),您实际上可以只使用#>,您只需在您的评论之后添加重新添加前两行(例如true --%等)#>评论
  4. \n
  5. 一种更为罕见的情况是您使用#来删除字符串的一部分(我敢打赌大多数人甚至不知道这是 bash 功能)。下面的示例代码\n https://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION
  6. \n
\n
var1=">blah"\necho ${var1#\xef\xbc\x9e}\n# ^ removes the > from var1\n
Run Code Online (Sandbox Code Playgroud)\n

要解决这个问题,还有其他方法可以从字符串开头删除字符,请使用它们。

\n


Tes*_*ler 7

有可能的; 我不知道这是多么兼容,但PowerShell将字符串视为文本并最终显示在屏幕上,Bash将它们视为命令并尝试运行它们,并且两者都支持相同的函数定义语法.因此,将函数名称放在引号中,只有Bash会运行它,将"exit"放在引号中,只有Bash才会退出.然后编写PowerShell代码.

NB.这是有效的,因为两个shell中的语法重叠,并且您的脚本很简单 - 运行命令并处理变量.如果您尝试使用更高级的脚本(if/then,for,switch,case等)用于任何一种语言,另一种可能会抱怨.

保存这个,dual.ps1因此PowerShell对此感到满意,chmod +x dual.ps1因此Bash将运行它

#!/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
Run Code Online (Sandbox Code Playgroud)

然后

./dual.ps1
Run Code Online (Sandbox Code Playgroud)

在任何一个系统上


编辑:您可以通过使用不同的前缀注释代码块来包含更复杂的代码,然后让每种语言过滤掉自己的代码eval(通常的安全警告适用于eval),例如使用此方法(结合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
Run Code Online (Sandbox Code Playgroud)