在 Windows 批处理和 Linux Bash 中运行的单个脚本?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17510688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Single script to run in both Windows batch and Linux Bash?
提问by Ondra ?i?ka
Is it possible to write a single script file which executes in both Windows (treated as .bat) and Linux (via Bash)?
是否可以编写在 Windows(视为 .bat)和 Linux(通过 Bash)中执行的单个脚本文件?
I know the basic syntax of both, but didn't figure out. It could probably exploit some Bash's obscure syntax or some Windows batch processor glitch.
我知道两者的基本语法,但没有弄清楚。它可能会利用一些 Bash 的晦涩语法或一些 Windows 批处理器故障。
The command to execute may be just a single line to execute other script.
要执行的命令可能只是一行来执行其他脚本。
The motivation is to have just a single application boot command for both Windows and Linux.
其动机是为 Windows 和 Linux 提供一个单一的应用程序引导命令。
Update:The need for system's "native" shell script is that it needs to pick the right interpreter version, conform to certain well-known environment variables etc. Installing additional environments like CygWin is not preferable - I'd like to keep the concept "download & run".
更新:系统“原生”shell 脚本的需要是它需要选择正确的解释器版本,符合某些众所周知的环境变量等。安装额外的环境,如 CygWin 不是可取的 - 我想保留这个概念“下载并运行”。
The only other language to consider for Windows is Windows Scripting Host - WSH, which is preset by default since 98.
Windows 唯一需要考虑的其他语言是 Windows Scripting Host - WSH,它从 98 开始就默认设置了。
采纳答案by binki
What I have done is use cmd's label syntax as comment marker. The label character, a colon (:
), is equivalent to true
in most POSIXish shells. If you immediately follow the label character by another character which can't be used in a GOTO
, then commenting your cmd
script should not affect your cmd
code.
我所做的是使用 cmd 的标签语法作为注释标记。标签字符,一个冒号 ( :
),相当于true
大多数 POSIXish shell。如果您紧跟标签字符后面不能在 中使用的另一个字符GOTO
,则注释您的cmd
脚本不应影响您的cmd
代码。
The hack is to put lines of code after the character sequence “:;
”. If you're writing mostly one-liner scripts or, as may be the case, can write one line of sh
for many lines of cmd
, the following might be fine. Don't forget that any use of $?
must be before your next colon :
because :
resets $?
to 0.
技巧是在字符序列“ :;
”之后放置几行代码。如果您主要编写单行脚本,或者在可能的情况下,可以sh
为多行编写一行cmd
,则以下内容可能没问题。不要忘记任何使用都$?
必须在下一个冒号之前,:
因为:
重置$?
为 0。
:; echo "Hi, I'm ${SHELL}."; exit $?
@ECHO OFF
ECHO I'm %COMSPEC%
A very contrived example of guarding $?
:
一个非常人为的保护示例$?
:
:; false; ret=$?
:; [ ${ret} = 0 ] || { echo "Program failed with code ${ret}." >&2; exit 1; }
:; exit
ECHO CMD code.
Another idea for skipping over cmd
code is to use heredocsso that sh
treats the cmd
code as an unused string and cmd
interprets it. In this case, we make sure that our heredoc's delimiter is both quoted (to stop sh
from doing any sort of interpretation on its contents when running with sh
) and starts with :
so that cmd
skips over it like any other line starting with :
.
跳过cmd
代码的另一个想法是使用heredocs,以便sh
将cmd
代码视为未使用的字符串并对其进行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%
Depending on your needs or coding style, interlacing cmd
and sh
code may or may not make sense. Using heredocs is one method to perform such interlacing. This could, however, be extended with the GOTO
technique:
根据您的需要或编码风格,隔行扫描cmd
和sh
编码可能有意义也可能没有意义。使用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%
Universal comments, of course, can be done with the character sequence : #
or :;#
. The space or semicolon are necessary because sh
considers #
to be part of a command name if it is not the first character of an identifier. For example, you might want to write universal comments in the first lines of your file before using the GOTO
method to split your code. Then you can inform your reader of why your script is written so oddly:
当然,通用注释可以用字符序列: #
或:;#
. 该空间或分号是必需的,因为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%
Thus, some ideas and ways to accomplish sh
and cmd
-compatible scripts without serious side effects as far as I know (and without having cmd
output '#' is not recognized as an internal or external command, operable program or batch file.
).
因此,就我所知(并且没有output ),一些想法和方法来完成sh
和cmd
兼容脚本而没有严重的副作用。cmd
'#' is not recognized as an internal or external command, operable program or batch file.
回答by npocmaka
you can try this:
你可以试试这个:
#|| goto :batch_part
echo $PATH
#exiting the bash part
exit
:batch_part
echo %PATH%
Probably you'll need to use /r/n
as a new line instead of a unix style.If I remember correct the unix new line is not interpreted as a new line by .bat
scripts.Another way is to create an #.exe
file in the path that does do nothing in similar manner as my answer here: Is it possible to embed and execute VBScript within a batch file without using a temporary file?
可能您需要使用/r/n
新行而不是 unix 样式。如果我记得正确,脚本不会将 unix 新行解释为新行。.bat
另一种方法是#.exe
在不执行任何操作的路径中创建一个文件与我在这里的回答类似的方式: 是否可以在不使用临时文件的情况下在批处理文件中嵌入和执行 VBScript?
EDIT
编辑
The binki's answeris almost perfect but still can be improved:
该binki的回答几乎是完美的,但仍然可以加以改进:
:<<BATCH
@echo off
echo %PATH%
exit /b
BATCH
echo $PATH
It uses again the :
trick and the multi line comment.Looks like cmd.exe (at least on windows10) works without problems with the unix style EOLs so be sure that your script is converted into linux format. (same approach has been seen used before hereand here) . Though using shebang still will produce redundant output...
它再次使用:
技巧和多行注释。看起来像 cmd.exe(至少在 windows10 上)在 unix 样式的 EOL 上没有问题,因此请确保您的脚本转换为 linux 格式。(之前在这里和这里已经看到使用了相同的方法)。虽然使用shebang仍然会产生冗余输出......
回答by u4370109
I wanted to comment, but can only add an answer at the moment.
我想发表评论,但目前只能添加一个答案。
The techniques given are excellent and I use them also.
给出的技术非常好,我也使用它们。
It is hard to retain a file which has two kinds of line breaks contained within it, that being /n
for the bash part and /r/n
for the windows part. Most editors try and enforce a common line break scheme by guessing what kind of file you are editing. Also most methods of transferring the file across the internet (particularly as a text or script file) will launder the line breaks, so you could start with one kind of line break and end up with the other. If you made assumptions about line breaks and then gave your script to someone else to use they might find it doesn't work for them.
很难保留包含两种换行符的文件,即/n
bash 部分和/r/n
windows 部分。大多数编辑器通过猜测您正在编辑的文件类型来尝试强制执行通用的换行符方案。此外,大多数通过互联网传输文件的方法(特别是作为文本或脚本文件)都会清洗换行符,因此您可以从一种换行符开始,以另一种结束。如果您假设了换行符,然后将您的脚本提供给其他人使用,他们可能会发现它对他们不起作用。
The other problem is network mounted file systems (or CDs) that are shared between different system types (particularly where you can't control the software available to the user).
另一个问题是在不同系统类型之间共享的网络安装文件系统(或 CD)(特别是在您无法控制用户可用的软件的情况下)。
One should therefore use the DOS line break of /r/n
and also protect the bash script from the DOS /r
by putting a comment at the end of each line (#
). You also cannot use line continuations in bash because the /r
will cause them to break.
因此,应该使用 DOS 换行符,/r/n
并/r
通过在每一行的末尾添加注释来保护 bash 脚本免受 DOS 攻击( #
)。您也不能在 bash 中使用换行符,因为这/r
会导致它们中断。
In this way whoever uses the script, and in whatever environment, it will then work.
通过这种方式,无论谁使用脚本,在任何环境中,它都将起作用。
I use this method in conjunction with making portable Makefiles!
我将此方法与制作便携式 Makefile 结合使用!
回答by Remik Ziemlinski
The following works for me without any errors or error messages with Bash 4 and Windows 10, unlike the answers above. I name the file "whatever.cmd", do chmod +x
to make it executable in linux, and make it have unix line endings (dos2unix
) to keep bash quiet.
与上面的答案不同,以下内容适用于 Bash 4 和 Windows 10,没有任何错误或错误消息。我将文件命名为“whatever.cmd”,chmod +x
使其在 linux 中可执行,并使其具有 unix 行结尾 ( dos2unix
) 以保持 bash 安静。
:; if [ -z 0 ]; then
@echo off
goto :WINDOWS
fi
if [ -z "" ]; then
echo "usage: # 2>nul & echo Hello cmd! & rem ^
echo 'Hello bash!' #
<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
回答by Tex Killer
There are several ways of executing different commands on bash
and cmd
with the same script.
有上执行不同的命令的几种方法bash
,并cmd
用相同的脚本。
cmd
will ignore lines that start with :;
, as mentioned in other answers. It will also ignore the next line if the current line ends with the command rem ^
, as the ^
character will escape the line break and the next line will be treated as a comment by rem
.
cmd
将忽略以 开头的行:;
,如其他答案中所述。如果当前行以命令结尾,它也将忽略下一行rem ^
,因为该^
字符将转义换行符并且下一行将被视为注释rem
。
As for making bash
ignore the cmd
lines, there are multiple ways. I have enumerated some ways to do that without breaking the cmd
commands:
至于bash
忽略这些cmd
行,有多种方法。我列举了一些在不破坏cmd
命令的情况下做到这一点的方法 :
Non-existent #
command (not recommended)
不存在的#
命令(不推荐)
If there is no #
command available on cmd
when the script is run, we can do this:
如果脚本运行时没有#
可用的命令cmd
,我们可以这样做:
echo >/dev/null # >nul & echo Hello cmd! & rem ^
echo 'Hello bash!' #
The #
character at the beginning of the cmd
line makes bash
treat that line as a comment.
该行#
开头的字符cmd
使bash
将该行视为注释。
The #
character at the end of the bash
line is used to comment out the \r
character, as Brian Tompsett pointed out in his answer. Without this, bash
will throw an error if the file has \r\n
line endings, required by cmd
.
正如布赖恩·汤普塞特 (Brian Tompsett) 在他的回答中指出的那样#
,该bash
行末尾的字符用于注释掉该\r
字符。如果没有这个,如果文件有行结尾,将抛出一个错误,需要.bash
\r\n
cmd
By doing # 2>nul
, we're tricking cmd
to ignore the error of some non-existent #
command, while still executing the command that follows.
通过这样做# 2>nul
,我们欺骗cmd
了忽略一些不存在#
命令的错误,同时仍然执行后面的命令。
Don't use this solution if there is a #
command available on the PATH
or if you have no control over the commands available to cmd
.
如果 上有#
可用命令,PATH
或者您无法控制cmd
.
Using echo
to ignore the #
character on cmd
使用echo
忽略#
人品cmd
We can use echo
with it's output redirected to insert cmd
commands on bash
's commented out area:
我们可以使用echo
它的输出重定向来cmd
在bash
的注释区域插入命令:
echo >/dev/null # 1>nul 2> #.bat
# & echo Hello cmd! & del #.bat & rem ^
echo 'Hello bash!' #
Since the #
character has no special meaning on cmd
, it is treated as a part of the text to echo
. All we had to do is redirect the output of the echo
command and insert other commands after it.
由于#
字符 on 没有特殊含义cmd
,因此将其视为文本的一部分 to echo
。我们所要做的就是重定向echo
命令的输出并在其后插入其他命令。
Empty #.bat
file
空#.bat
文件
:; echo 'Hello bash!';<<:
echo Hello cmd! & ^
:
The echo >/dev/null # 1>nul 2> #.bat
line creates an empty #.bat
file while on cmd
(or replaces existing #.bat
, if any), and does nothing while on bash
.
该echo >/dev/null # 1>nul 2> #.bat
行在#.bat
on 时创建一个空文件cmd
(或替换现有的#.bat
,如果有),并且在 on 时不执行任何操作bash
。
This file will be used by the cmd
line(s) that follows even if there is some other #
command on the PATH
.
该文件将被使用cmd
的是如下即使有一些其他的线(S)#
上的命令PATH
。
The del #.bat
command on the cmd
-specific code deletes the file that was created. You only have to do this on the last cmd
line.
在del #.bat
该命令cmd
特异性代码删除已创建的文件。您只需在最后cmd
一行执行此操作。
Don't use this solution if a #.bat
file could be on your current working directory, as that file will be erased.
如果#.bat
文件可能位于您当前的工作目录中,请不要使用此解决方案,因为该文件将被删除。
Recomended: using here-documentto ignore cmd
commands on bash
推荐:使用here-document忽略cmd
命令bash
:;( #
:; echo 'Hello' #
:; echo 'bash!' #
:; );<<'here-document delimiter'
(
echo Hello
echo cmd!
) & rem ^
here-document delimiter
By placing the ^
character at the end of the cmd
line we're escaping the line break, and by using :
as the here-document delimiter, the delimiter line contents will have no effect on cmd
. That way, cmd
will only execute its line after the :
line is over, having the same behaviour as bash
.
通过将^
字符放在行尾,cmd
我们将转义换行符,并且通过:
用作 here-document 分隔符,分隔符行内容将不会影响cmd
. 这样,cmd
只会在行结束后执行其行:
,具有与bash
.
If you want to have multiple lines on both platforms and only execute them at the end of the block, you can do this:
如果你想在两个平台上都有多行并且只在块的末尾执行它们,你可以这样做:
:;SET() { eval ; }
SET var=value
:;echo $var
:;exit
ECHO %var%
As long as there is no cmd
line with exactly here-document delimiter
, this solution should work. You can change here-document delimiter
to any other text.
只要不cmd
与 完全一致here-document delimiter
,此解决方案就应该有效。您可以更改here-document delimiter
为任何其他文本。
In all of the presented solutions, the commands will only be executed after the last line, making their behaviour consistent if they do the same thing on both platforms.
在所有提出的解决方案中,命令只会在最后一行之后执行,如果它们在两个平台上做同样的事情,它们的行为就会保持一致。
Those solutions must be saved to files with \r\n
as line breaks, otherwise they won't work on cmd
.
这些解决方案必须以\r\n
换行符的形式保存到文件中,否则它们将无法在cmd
.
回答by Velkan
You can share variables:
您可以共享变量:
echo >/dev/null # >nul & GOTO WINDOWS & rem ^
echo 'Processing for Linux'
# ***********************************************************
# * NOTE: If you modify this content, be sure to remove carriage returns (\r)
# * from the Linux part and leave them in together with the line feeds
# * (\n) for the Windows part. In summary:
# * New lines in Linux: \n
# * New lines in Windows: \r\n
# ***********************************************************
# Do Linux Bash commands here... for example:
StartDir="$(pwd)"
# Then, when all Linux commands are complete, end the script with 'exit'...
exit 0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
:WINDOWS
echo "Processing for Windows"
REM Do Windows CMD commands here... for example:
SET StartDir=%cd%
REM Then, when all Windows commands are complete... the script is done.
回答by A-Diddy
The previous answers seem to cover pretty much all the options and helped me a lot. I'm including this answer here just to demonstrate the mechanism I used to include both a Bash script and a Windows CMD script in the same file.
以前的答案似乎涵盖了几乎所有选项,对我帮助很大。我在这里包含这个答案只是为了演示我用来在同一个文件中包含 Bash 脚本和 Windows CMD 脚本的机制。
LinuxWindowsScript.bat
LinuxWindowsScript.bat
#!/usr/bin/env sh
@ 2>/dev/null # 2>nul & echo off
:; alias ::=''
:: exec java -jar $JAVA_OPTS "common.inc
----------
common statement1
common statement2
" "$@"
:: exit
java -jar %JAVA_OPTS% "%~dpnx0" %*
exit /B
Summary
概括
In Linux
在 Linux 中
The first line (echo >/dev/null # >nul & GOTO WINDOWS & rem ^
) will be ignored and the script will flow through each line immediately following it until the exit 0
command is executed. Once exit 0
is reached, the script execution will end, ignoring the Windows commands below it.
第一行 ( echo >/dev/null # >nul & GOTO WINDOWS & rem ^
) 将被忽略,脚本将紧随其后的每一行,直到exit 0
命令被执行。一旦exit 0
到达,脚本执行将结束,忽略它下面的 Windows 命令。
In Windows
在 Windows 中
The first line will execute the GOTO WINDOWS
command, skipping the Linux commands immediately following it and continuing execution at the :WINDOWS
line.
第一行将执行GOTO WINDOWS
命令,跳过紧随其后的 Linux 命令并在该:WINDOWS
行继续执行。
Removing Carriage Returns in Windows
在 Windows 中删除回车符
Since I was editing this file in Windows, I had to systematically remove the carriage returns (\r) from the Linux commands or else I got abnormal results when running the Bash portion. To do this, I opened the file in Notepad++and did the following:
由于我是在 Windows 中编辑此文件,因此我必须系统地从 Linux 命令中删除回车符 (\r),否则在运行 Bash 部分时会出现异常结果。为此,我在Notepad++ 中打开了该文件并执行了以下操作:
Turn on the option for viewing end of line characters (
View
>Show Symbol
>Show End of Line
). Carriage returns will then show asCR
characters.Do a Find & Replace (
Search
>Replace...
) and check theExtended (\n, \r, \t, \0, \x...)
option.Type
\r
in theFind what :
field and blank out theReplace with :
field so there's nothing in it.Starting at the top of the file, click the
Replace
button until all of the carriage return (CR
) characters have been removed from the top Linux portion. Be sure to leave the carriage return (CR
) characters for the Windows portion.
打开用于查看行尾字符 (
View
>Show Symbol
>Show End of Line
) 的选项。然后回车将显示为CR
字符。执行查找和替换 (
Search
>Replace...
) 并选中该Extended (\n, \r, \t, \0, \x...)
选项。\r
在该Find what :
字段中键入并将该字段清空,Replace with :
以便其中没有任何内容。从文件顶部开始,单击
Replace
按钮,直到CR
从顶部 Linux 部分删除所有回车 ( ) 字符。确保CR
为 Windows 部分保留回车 ( ) 字符。
The result should be that each Linux command ends in just a line feed (LF
) and each Windows command ends in a carriage return and line feed (CR
LF
).
结果应该是每个 Linux 命令都以换行符 ( LF
) 结尾,而每个 Windows 命令都以回车符和换行符 ( CR
LF
)结尾。
回答by LolHens
I use this technique to create runnable jar files. Since the jar/zip file starts at the zip header, I can put a universal script to run this file at the top:
我使用这种技术来创建可运行的 jar 文件。由于 jar/zip 文件从 zip 头开始,我可以在顶部放置一个通用脚本来运行此文件:
linux.sh
--------
# do linux specific stuff
...
# call common code
source common.inc
- The first line does echo off in cmd and doesn't print anything on sh. This is because the
@
in sh throws an error that is piped to/dev/null
and after that a comment starts. On cmd the pipe to/dev/null
fails because the file is not recognized on windows but since windows doesn't detect#
as a comment the error is piped tonul
. Then it does an echo off. Because the whole line is preceded by an@
it doesn't get printet on cmd. - The second one defines
::
, which starts a comment in cmd, to noop in sh. This has the benefit that::
does not reset$?
to0
. It uses the ":;
is a label" trick. - Now I can prepend sh commands with
::
and they are ignored in cmd - On
:: exit
the sh script ends and I can write cmd commands - Only the first line (shebang) is problematic in cmd since it will print
command not found
. You have to decide yourself if you need it or not.
- 第一行确实在 cmd 中回显,并且不会在 sh 上打印任何内容。这是因为
@
in sh 引发了一个错误,该错误通过管道传输到/dev/null
注释开始之后。在 cmd 上,管道/dev/null
失败,因为该文件在 windows 上无法识别,但由于 windows 未检测#
为注释,因此错误通过管道传输到nul
. 然后它会发出回声。因为整行前面都有一个@
它不会在 cmd 上打印。 - 第二个定义
::
,在 cmd 中开始注释,在 sh 中 noop。这具有::
不会重置$?
为的好处0
。它使用“:;
是一个标签”技巧。 - 现在我可以在 sh 命令前面加上
::
它们在 cmd 中被忽略 - 在
:: exit
sh 脚本结束时,我可以编写 cmd 命令 - 只有第一行(shebang)在 cmd 中有问题,因为它会打印
command not found
. 你必须自己决定是否需要它。
回答by Shital Shah
I needed this for some of my Python package install scripts. Most things between sh and bat file are same but few things like error handling are different. One way to do this is as follows:
我的一些 Python 包安装脚本需要这个。sh 和 bat 文件之间的大部分内容是相同的,但很少有诸如错误处理之类的内容不同。一种方法如下:
windows.bat
-----------
REM do windows specific things
...
# call common code
call common.inc
Then you call this from bash script:
然后你从 bash 脚本调用它:
##代码##Windows batch file looks like this:
Windows 批处理文件如下所示:
##代码##回答by skanga
Try my BashWin project at https://github.com/skanga/bashwinwhich uses BusyBox for most Unix commands
在https://github.com/skanga/bashwin 上试试我的 BashWin 项目,它对大多数 Unix 命令使用 BusyBox