WindowsバッチとLinux Bashの両方で実行する単一のスクリプト?


96

Windows(.batとして扱われる)とLinux(Bash経由)の両方で実行される単一のスクリプトファイルを書くことは可能ですか?

私は両方の基本的な構文を知っていますが、わかりませんでした。おそらく、Bashのあいまいな構文やWindowsバッチプロセッサの不具合を悪用する可能性があります。

実行するコマンドは、他のスクリプトを実行するための単一行である場合があります。

その動機は、WindowsとLinuxの両方で単一のアプリケーションブートコマンドを使用することです。

更新:システムの「ネイティブ」シェルスクリプトの必要性は、適切なインタープリターバージョンを選択し、特定の既知の環境変数などに準拠する必要があることです。CygWinのような追加の環境をインストールすることは好ましくありません。コンセプトを維持したいと思います。ダウンロードして実行」。

Windowsで考慮すべき他の唯一の言語は、Windows Scripting Host-WSHです。これは、98以降、デフォルトで事前設定されています。


9
PerlまたはPythonを調べます。これらは両方のプラットフォームで利用可能なスクリプト言語です。
a_horse_with_no_name 2013

グルーヴィー、パイソン、ルビー誰か? stackoverflow.com/questions/257730/...
Kalpesh曽爾

Groovyは、デフォルトですべてのシステムにインストールできると便利です。私はそれをシェルスクリプト言語として使用します。たぶんいつか:)
OndraŽiOctka 2016年

1
次も参照してください:github.com/BYVoid/Batsh
Dave Jarvis

2
これの説得力のある使用例はチュートリアルです。「Windowsの場合はこの小さなスクリプトを使用しますが、LinuxまたはMacの場合はこれを試してください。代わりに、私はWindowsを使用しているため、実際にはテストしていません。」悲しいことに、Windowsにはcp組み込みのようなUNIXに似た基本的なコマンドがない、またはその逆なので、2つの個別のスクリプトを書く方が、ここに示す高度な手法よりも教育的に優れている場合があります。
Qwertie

回答:


92

私がやったことは、コメントマーカーとしてcmdのラベル構文を使用することです。ラベル文字のコロン(:)はtrue、ほとんどのPOSIXishシェルと同等です。ラベル文字の直後に、で使用できない別の文字がGOTO続いている場合は、cmdスクリプトにコメントを付けてもcmdコードには影響しません。

ハックは、文字列「:;」の後にコード行を配置することです。あなたは主にワンライナースクリプトを書いたりしている場合場合のように、の1行書くことができsh、多くのラインのためにcmd、次のことがあるかもしれない罰金を。は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コードをスキップする別のアイデアは、ヒアドキュメントを使用しshて、cmdコードを未使用の文字列として扱い、cmd解釈することです。この場合、ヒアドキュメントの区切り文字が引用符で囲まれ(で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コードは意味がある場合と意味がない場合があります。ヒアドキュメントを使用することは、このようなインターレースを実行する1つの方法です。ただし、これは次の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%

もちろん、普遍的なコメントは、文字シーケンス: #orで行うことができます:;#。スペースまたはセミコロンは、識別子の最初の文字でない場合はコマンド名の一部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 /programming/17510688
: # for details.
:; echo "This is ${SHELL}"; exit
@ECHO OFF
ECHO This is %COMSPEC%

したがって、私が知る限り、重大な副作用なしに(そして出力を持たずに)shcmd互換性のあるスクリプトを実現するためのいくつかのアイデアと方法。cmd'#' is not recognized as an internal or external command, operable program or batch file.


5
これにはスクリプトに.cmd拡張子を付ける必要があることに注意してください。拡張子がないと、Windowsでは実行できません。回避策はありますか?
jviotti 2015年

1
バッチファイルを作成している場合は、名前を付け.cmdて実行可能としてマークすることをお勧めします。そうすれば、Windowsまたはunixのデフォルトシェルからスクリプトを実行できます(bashでのみテスト)。cmdに大声で文句を言わせずにシバンを含める方法は考えられないため、常にシェルを介してスクリプトを呼び出す必要があります。execlp()つまり、or ではなくor を使用するようにしてくださいexecvp()system()これは「正しい」方法だと思います)。後者の関数は、OSに直接移動するため、シバンのあるスクリプトでのみ機能します。execl()execv()
binki 2015年

ポータブルコードを独立したバッチファイルとしてではなく、シェルアウト(例:<Exec/>MSBuild Task)で記述していたため、ファイル拡張子についての説明は省略しました。将来的に時間があれば、これらのコメントの情報で回答を更新します…
binki

23

あなたはこれを試すことができます:

#|| goto :batch_part
 echo $PATH
#exiting the bash part
exit
:batch_part
 echo %PATH%

おそらくあなたは/r/nUNIXスタイルの代わりに新しい行として使用する必要があるでしょう。私が正しいと覚えていれば、UNIXの新しい行は.batスクリプトによって新しい行として解釈されません。別の方法は、#.exe何もしないパスにファイルを作成することですここで私の答えと同様の方法: 一時ファイルを使用せずにバッチファイル内にVBScriptを埋め込み、実行することは可能ですか?

編集する

binkiの答えはほぼ完璧ですが、まだ改善することができます。

:<<BATCH
    @echo off
    echo %PATH%
    exit /b
BATCH

echo $PATH

もう一度:トリックと複数行のコメントを使用します.cmd.exe(少なくともwindows10では)のように見えますが、UNIXスタイルのEOLでは問題なく動作するため、スクリプトがLinux形式に変換されていることを確認してください。(ここここで同じアプローチが使用されているのが見られます)。シバンを使用すると、冗長な出力が生成されますが...


3
/r/n行の終わりに#(つまり#/r/n)で各行を終了しない限り、bashスクリプトで多くの頭痛の種が発生します-このよう\rに、コメントの一部として扱われ、無視されます。
Gordon Davisson 2013

2
実際、私# 2>NUL & ECHO Welcome to %COMSPEC%.はが#コマンドが見つからないことに関するエラーをなんとか隠すことができますcmd。この手法は、cmd(たとえば、Makefile)によってのみ実行されるスタンドアロン行を記述する必要がある場合に役立ちます。
binki 2014年

11

コメントしたかったのですが、現時点では回答しか追加できません。

与えられた技術は優れており、私もそれらを使用しています。

/nbash部分と/r/nwindows部分の2種類の改行が含まれているファイルを保持するのは困難です。ほとんどのエディターは、編集しているファイルの種類を推測することにより、一般的な改行スキームを適用しようとします。また、インターネットを介してファイルを転送するほとんどの方法(特にテキストファイルまたはスクリプトファイル)は改行を洗浄するため、ある種類の改行から始めて、もう一方の行で終わる可能性があります。改行について想定してから、スクリプトを他の人に渡して使用してもらえない場合があります。

もう1つの問題は、異なるシステムタイプ間で共有されるネットワークマウントファイルシステム(またはCD)です(特に、ユーザーが使用できるソフトウェアを制御できない場合)。

したがって、DOSの改行を使用し、各行の最後にコメントを付けることで/r/nbashスクリプトをDOSから保護する必要があります/r#)。また、bashで行の継続を使用することはできません/r

このようにして、誰でもスクリプトを使用し、どのような環境でもスクリプトは機能します。

この方法は、移植可能なMakefileの作成と組み合わせて使用​​します。


6

上記の回答とは異なり、Bash 4とWindows 10でエラーやエラーメッセージが表示されることはありません。ファイルに「whatever.cmd」という名前を付けてchmod +x、Linuxで実行可能にし、dos2unixbashを静かに保つためにUNIXの行末()を付けます。

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

3

変数を共有できます。

:;SET() { eval $1; }

SET var=value

:;echo $var
:;exit
ECHO %var%

2

同じスクリプト上bashcmd同じスクリプトで異なるコマンドを実行する方法はいくつかあります。

cmd:;他の回答で述べたように、で始まる行は無視されます。文字が改行をエスケープし、次の行がによってコメントとして扱われるため、現在の行がコマンドrem ^で終了する場合も、次の行は無視^されremます。

行をbash無視させるcmdには、いくつかの方法があります。cmdコマンドを壊すことなくそれを行ういくつかの方法を列挙しました :

存在しない#コマンド(非推奨)

スクリプトの実行時に#使用できるコマンドがない場合は、次のようにcmdできます。

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

#の先頭の文字は、その行をコメントとして扱います。cmdbash

#行末の文字は、ブライアントンプセットが回答で指摘したbashように、\r文字をコメント化するために使用されます。これbashがないと、ファイルの\r\n行末にが必要な場合にエラーがスローされcmdます。

を実行することにより、後続のコマンドを実行しながら、存在しないコマンドのエラーを無視するように# 2>nul仕向けcmd#います。

で使用#できるコマンドがあるPATH場合、またはで使用できるコマンドを制御できない場合は、このソリューションを使用しないでくださいcmd


を使用echoして#文字を無視するcmd

我々は使用することができecho、出力を挿入するようにリダイレクトされ、それのとcmdでコマンドをbashさん、コメントアウトエリア:

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

その#文字はで特別な意味を持たないため、cmdへのテキストの一部として扱われますechoechoコマンドの出力をリダイレクトし、その後に他のコマンドを挿入するだけで済みました。


空の#.batファイル

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

このecho >/dev/null # 1>nul 2> #.bat行は、#.batオンの間は空のファイルを作成しcmd(または、存在する#.bat場合は既存のを置き換え)、オンの間は何もしませんbash

このファイルcmdは、に他の#コマンドがある場合でも、後続の行で使用されますPATH

-specificコードのdel #.batコマンドは、cmd作成されたファイルを削除します。これは最後のcmd行で行うだけです。

#.batファイルが現在の作業ディレクトリにある可能性がある場合は、このソリューションを使用しないでください。そのファイルは消去されます。


推奨:ヒアドキュメントを使用しcmdコマンドを無視するbash

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

^行末に文字を配置することにより、cmd改行をエスケープ:し、ヒアドキュメントの区切り文字として使用することで、区切り文字の行の内容はに影響しませんcmd。この方法でcmdは、:行が終了した後にのみその行を実行し、と同じ動作をしbashます。

両方のプラットフォームで複数の行を使用し、それらをブロックの最後でのみ実行する場合は、次のようにします。

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

cmdと正確here-document delimiterに一致する行がない限り、このソリューションは機能します。here-document delimiter他のテキストに変更できます。


提示されたすべてのソリューションでは、コマンドは最後の行の後でのみ実行され、両方のプラットフォームで同じことを実行した場合の動作に一貫性があります。

これらのソリューションは\r\n、改行としてファイルに保存する必要がありますcmd。そうしないと、機能しません。


遅刻するよりはましです...これは他の答えよりも私を助けました。ありがとう!!
A-ディディ

2

以前の回答はほとんどすべてのオプションをカバーしているようで、私を大いに助けました。ここでは、同じファイルにBashスクリプトとWindows CMDスクリプトの両方を含めるために使用したメカニズムを示すために、この回答を含めています。

LinuxWindowsScript.bat

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.

概要

Linuxの場合

最初の行(echo >/dev/null # >nul & GOTO WINDOWS & rem ^)は無視され、exit 0コマンドが実行されるまで、スクリプトはその直後の各行を流れます。一度exit 0到達すると、スクリプトの実行は、それ以下のWindowsコマンドを無視して、終了します。

Windowsの場合

最初の行はGOTO WINDOWSコマンドを実行し、その直後のLinuxコマンドをスキップして、:WINDOWS行でます。

Windowsでキャリッジリターンを削除する

Windowsでこのファイルを編集していたため、Linuxコマンドからキャリッジリターン(\ r)を体系的に削除する必要がありました。そうしないと、Bash部分を実行すると異常な結果が出ました。これを行うために、ファイルをNotepad ++開き、次のことを行いました。

  1. 行末文字を表示するためのオプションをオンにします(View> Show Symbol> Show End of Line)。キャリッジリターンはCR文字として表示されます。

  2. 検索と置換(Search> Replace...)を実行し、Extended (\n, \r, \t, \0, \x...)オプションを確認します。

  3. フィールドに入力\rし、Find what :フィールドを空白にして、Replace with :何もないようにします。

  4. ファイルの先頭から始めて、Replaceすべてのキャリッジリターン(CR)文字がLinuxの上部から削除されるまでボタンをクリックします。CRWindows部分には、必ずキャリッジリターン()文字を残してください。

その結果、各Linuxコマンドは改行(LF)だけで終わり、各Windowsコマンドは復帰と改行(CR LF)で終わります。


1

この手法を使用して、実行可能なjarファイルを作成します。jar / zipファイルはzipヘッダーで始まるため、このファイルを実行するユニバーサルスクリプトを上部に配置できます。

#!/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
  • 最初の行はcmdでエコーオフし、shには何も出力しません。これは、@in shがエラーをスローし、パイプでパイプさ/dev/nullれた後にコメントが開始されるためです。cmdでは、/dev/nullWindowsでファイルが認識されないためパイプが失敗しますが、Windowsは#コメントとして検出されないため、エラーはパイプされnulます。次に、エコーをオフにします。行全体の前にがあるため、@cmdでprintetを取得しません。
  • 2つ目は::、cmdでコメントを開始するを、shでnoopに定義します。これには、に::リセットさ$?れないという利点があり0ます。「:;ラベル」というトリックを使用しています。
  • 今私はshコマンドを前に付けることができ::、それらはcmdでは無視されます
  • オン :: exit shスクリプトが終了し、私はcmdのコマンドを書くことができます
  • 最初の行(シバン)だけが表示されるため、cmdで問題があります command not foundです。必要かどうかは自分で決める必要があります。

0

一部のPythonパッケージインストールスクリプトにこれが必要でした。shとbatファイルの間のほとんどのものは同じですが、エラー処理などのいくつかの点が異なります。これを行う1つの方法は次のとおりです。

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

次に、これをbashスクリプトから呼び出します。

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

Windowsバッチファイルは次のようになります。

windows.bat
-----------
REM do windows specific things
...
# call common code
call common.inc


-6

AntやMavenのようなxml構文(Javaベース)を備えたプラットフォームに依存しないビルドツールがあります。そのため、osタイプにもかかわらず、AntまたはMavenですべてのスクリプトを書き直して実行することができます。または、osタイプを分析して適切なbatまたはbashスクリプトを実行するAntラッパースクリプトを作成することもできます。


5
これは、これらのツールの重大な誤用のようです。実際の質問(ネイティブシェルで実行中)を回避したい場合は、Pythonなどの汎用スクリプト言語を提案する必要があります。適切なスクリプト言語の代わりとして誤用される可能性があるビルドツールではありません。
ArtOfWarfare 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.