单个脚本在Windows批处理和Linux Bash中运行?

是否有可能编写一个脚本文件在Windows(视为.bat)和Linux(通过Bash)执行?

我知道两者的基本语法,但没有弄清楚。 它可能会利用一些Bash的晦涩语法或一些Windows批处理器故障。

要执行的命令可能只是一行来执行其他脚本。

动机是为Windows和Linux提供一个应用程序启动命令。

更新:系统的“原生”shell脚本的需要是,它需要select正确的解释器版本,符合某些众所周知的环境variables等安装额外的环境,如CygWin不是最好的 – 我想保持“下载并运行“。

Windows中唯一需要考虑的其他语言是Windows Scripting Host – WSH,它自98年起默认预设。

我所做的是使用cmd的标签语法作为注释标记 。 在大多数POSIXshell中,标号字符(冒号(:))等同于true 。 如果您立即按照GOTO不能使用的另一个字符来追踪标签字符,则注释您的cmd脚本不应影响您的cmd代码。

黑客就是在字符序列之后加上一行代码“ :; ”。 如果你写的主要是单线脚本,或者可能是这样的话,可以为许多cmd行编写一行sh ,以下情况可能没有问题。 不要忘记使用$? 必须在你的下一个冒号之前:因为:重置$? 为0。

 :; echo "Hi, I'm ${SHELL}."; exit $? @ECHO OFF ECHO I'm %COMSPEC% 

守护$?一个非常人为的例子$?

 :; false; ret=$? :; [ ${ret} = 0 ] || { echo "Program failed with code ${ret}." >&2; exit 1; } :; exit ECHO CMD code. 

跳过cmd代码的另一个想法是使用heredocs,以便shcmd代码视为一个未使用的string, cmd解释它。 在这种情况下,我们确保我们的heredoc的分隔符被引用(在用sh运行时阻止sh对其内容进行任何解释),并且从以下开始:以便cmd像其他任何行一样跳过:

 :; echo "I am ${SHELL}" :<<"::CMDLITERAL" ECHO I am %COMSPEC% ::CMDLITERAL :; echo "And ${SHELL} is back!" :; exit ECHO And back to %COMSPEC% 

根据您的需要或编码风格,隔行扫描cmdsh代码可能有或没有意义。 使用heredocs是执行这种交错的一种方法。 但是,这可以通过GOTO技术来扩展:

 :<<"::CMDLITERAL" @ECHO OFF GOTO :CMDSCRIPT ::CMDLITERAL echo "I can write free-form ${SHELL} now!" if :; then echo "This makes conditional constructs so much easier because" echo "they can now span multiple lines." fi exit $? :CMDSCRIPT ECHO Welcome to %COMSPEC% 

通用注释当然可以用字符序列来完成: #:;# 。 如果sh不是标识符的第一个字符, sh认为#是命令名的一部分,所以需要空格或分号。 例如,在使用GOTO方法拆分代码之前,可能需要在文件的第一行写入通用注释。 那么你可以告诉你的读者为什么你的脚本写得这么奇怪:

 : # This is a special script which intermixes both sh : # and cmd code. It is written this way because it is : # used in system() shell-outs directly in otherwise : # portable code. See https://stackoverflow.com/questions/17510688 : # for details. :; echo "This is ${SHELL}"; exit @ECHO OFF ECHO This is %COMSPEC% 

因此,就我所知(没有cmd输出'#' is not recognized as an internal or external command, operable program or batch file.'#' is not recognized as an internal or external command, operable program or batch file.没有严重副作用的一些想法和方法来完成shcmd兼容脚本。

你可以试试这个:

 #|| goto :batch_part some unix commands #exiting the bash part exit :batch_part some windows commands 

也许你需要使用/r/n作为一个新行,而不是一个unix style.If我记得正确的unix新行不会被解释为一个新的行.bat脚本。另一种方法是创build一个#.exe文件的path中没有做任何事情与我的答案类似的方式在这里: 是否有可能在一个batch file中embedded和执行VBScript而不使用临时文件?

我想评论,但目前只能添加一个答案。

给出的技术是非常好的,我也使用它们。

很难保留其中包含两种换行符的文件,即/n为bash部分, /r/n为windows部分。 大多数编辑试图通过猜测你正在编辑什么样的文件来执行一个普通的换行符。 另外,通过互联网传输文件的大多数方法(特别是作为文本或脚本文件)将清洗换行符,因此您可以从一种换行开始,结束另一种换行。 如果你对换行做了假设,然后把你的脚本给别人使用,他们可能会发现它不适合他们。

另一个问题是在不同系统types之间共享的networking安装文件系统(或CD)(尤其是在无法控制用户可用软件的情况下)。

因此,应该使用/r/n的DOS换行符,并在每行( # )的末尾添加注释以保护DOS /r的bash脚本。 你也不能在bash中使用连续行,因为/r会导致它们中断。

用这种方式,谁使用脚本,并在任何环境,它将然后工作。

我将这种方法与制作便携式Makefiles结合使用!

下面的工作对我来说没有任何错误或Bash 4和Windows 10的错误消息,不像上面的答案。 我命名文件“whatever.cmd”,做chmod +x使它在linux中可执行,并使它具有unix行结尾( dos2unix )保持安静。

 :; if [ -z 0 ]; then @echo off goto :WINDOWS fi if [ -z "$2" ]; then echo "usage: $0 <firstArg> <secondArg>" exit 1 fi # bash stuff exit :WINDOWS if [%2]==[] ( SETLOCAL enabledelayedexpansion set usage="usage: %0 <firstArg> <secondArg>" @echo !usage:"=! exit /b 1 ) :: windows stuff 

有几种方法可以在bashcmd用同一个脚本执行不同的命令。

cmd会忽略以下行开头的行:; 正如其他答案中提到的那样。 如果当前行以rem ^命令结束,它也将忽略下一行,因为^字符将跳过换行符,下一行将被视为rem的注释。

至于让bash忽略cmd行,有多种方法。 我列举了一些不破坏cmd命令的方法:

不存在的#命令(不推荐)

如果脚本运行时没有#命令可用,我们可以这样做:

 # 2>nul & echo Hello cmd! & rem ^ echo 'Hello bash!' # 

cmd行开始处的#字符使bash将该行视为注释。

正如Brian Tompsett在回答中指出的那样, bash行末尾的#字符用于注释\r字符。 如果没有这个,如果文件有\r\n行尾, bash将会抛出一个错误,这是cmd要求的。

通过# 2>nul ,我们欺骗cmd忽略某个不存在的#命令的错误,同时仍然执行下面的命令。

如果PATH上有可用命令,或者您无法控制可用于cmd的命令,请不要使用此解决scheme。


使用echo来忽略cmd上的#字符

我们可以使用echo的输出redirect来在bash的注释区插入cmd命令:

 echo >/dev/null # >nul & echo Hello cmd! & rem ^ echo 'Hello bash!' # 

由于#字符在cmd上没有特别的含义,所以它被当作文本的一部分echo 。 我们所要做的就是redirectecho命令的输出,并在其后插入其他命令。


清空#.bat文件

 echo >/dev/null # 1>nul 2> #.bat # & echo Hello cmd! & del #.bat & rem ^ echo 'Hello bash!' # 

echo >/dev/null # 1>nul 2> #.bat行在cmd上创build一个空的#.bat文件(或者replace现有的#.bat ,如果有的话),而在bash中什么都不做。

这个文件将被cmd行使用,即使在PATH上有一些其他的命令。

cmd特定代码上的del #.bat命令将删除已创build的文件。 你只需要在最后一行cmd上执行此操作。

如果#.bat文件可能位于当前工作目录下,则不要使用此解决scheme,因为该文件将被删除。


推荐:使用here-document忽略bash cmd命令

 :; echo 'Hello bash!';<<: echo Hello cmd! & ^ : 

通过将^字符放在cmd行的末尾,我们将转义换行符,并使用:作为here-document分隔符,分隔符行的内容对cmd没有任何影响。 这样, cmd将只在行结束后执行它的行,与bash具有相同的行为。

如果你想在两个平台上有多行,只在块的最后执行它们,你可以这样做:

 :;( # :; echo 'Hello' # :; echo 'bash!' # :; );<<'here-document delimiter' ( echo Hello echo cmd! ) & rem ^ here-document delimiter 

只要没有cmd行与完全here-document delimiter ,这个解决scheme应该工作。 您可以here-document delimiter更改here-document delimiter到任何其他文本。


在所有提出的解决scheme中, 这些命令只会在最后一行之后执行 ,如果它们在两个平台上执行相同的操作,则它们的行为将保持一致。

这些解决scheme必须以\r\n作为换行符保存到文件中,否则它们将不能在cmd

你可以分享variables:

 :;SET() { eval $1; } SET var=value :;echo $var :;exit ECHO %var% 

有一个独立于平台的构build工具,如Ant或Maven,带有xml语法(基于Java)。 所以,你可以重写所有脚本在Ant或Maven中运行,尽pipeostypes。 或者你可以创buildAnt包装脚本,它将分析操作系统的types,并运行适当的蝙蝠或bash脚本。