Single script to run in both Windows batch and Linux Bash?

后端 未结 11 1863
盖世英雄少女心
盖世英雄少女心 2020-11-28 19:04

Is it possible to write a single script file which executes in both Windows (treated as .bat) and Linux (via Bash)?

I know the basic syntax of both, but didn\'t figu

相关标签:
11条回答
  • 2020-11-28 19:45

    Try my BashWin project at https://github.com/skanga/bashwin which uses BusyBox for most Unix commands

    0 讨论(0)
  • 2020-11-28 19:47

    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.

    :; 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
    
    0 讨论(0)
  • 2020-11-28 19:49

    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.

    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.

    :; 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 heredocs so 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 :.

    :; 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:

    :<<"::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:

    : # 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.).

    0 讨论(0)
  • 2020-11-28 19:49

    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:

    common.inc
    ----------
    common statement1
    common statement2
    

    Then you call this from bash script:

    linux.sh
    --------
    # do linux specific stuff
    ...
    # call common code
    source common.inc
    

    Windows batch file looks like this:

    windows.bat
    -----------
    REM do windows specific things
    ...
    # call common code
    call common.inc
    
    0 讨论(0)
  • 2020-11-28 20:00

    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:

    #!/usr/bin/env sh\n
    @ 2>/dev/null # 2>nul & echo off & goto BOF\r\n
    :\n
    <shell commands go here with \n line endings>
    exit\n
    \r\n
    :BOF\r\n
    <cmd commands go here with \r\n line endings>\r\n
    exit /B %errorlevel%\r\n
    }
    

    It is important to set the line endings as outlined above because they can cause issues on the different platforms. Also the goto statement will not work correctly in some cases if the proper line endings are missing around the jump label.

    The technique above is what I use currently. Below is an outdated version with an in-depth explaination:

    #!/usr/bin/env sh
    @ 2>/dev/null # 2>nul & echo off
    :; alias ::=''
    :: exec java -jar $JAVA_OPTS "$0" "$@"
    :: exit
    java -jar %JAVA_OPTS% "%~dpnx0" %*
    exit /B
    
    • 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 to nul. 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 $? to 0. 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.
    0 讨论(0)
提交回复
热议问题