Windows(.batとして処理)とLinux(Bash経由)の両方で実行される単一のスクリプトファイルを作成することはできますか?
両方の基本的な構文は知っていますが、わかりませんでした。 Bashの不明瞭な構文やWindowsバッチプロセッサの不具合を悪用する可能性があります。
実行するコマンドは、他のスクリプトを実行するための1行だけです。
動機は、WindowsとLinuxの両方で単一のアプリケーションブートコマンドを使用することです。
更新:システムの「ネイティブ」シェルスクリプトの必要性は、適切なインタープリターバージョンを選択し、特定の既知の環境変数などに準拠する必要があることです。CygWinなどの追加環境のインストールは好ましくありません-私はd「ダウンロードして実行」という概念を維持したい。
Windowsで考慮すべき他の言語は、Windows Scripting Host-WSHのみです。これは、98年以降デフォルトで事前設定されています。
私がやったことは cmdのラベル構文をコメントマーカーとして使用 です。ラベル文字であるコロン(:
)は、ほとんどのPOSIXishシェルのtrue
と同等です。ラベル文字の直後にGOTO
で使用できない別の文字が続く場合、cmd
スクリプトにコメントを付けてもcmd
コードに影響はありません。
ハックは、文字列「:;
」の後にコードの行を置くことです。主に1行のスクリプトを記述している場合、または場合によっては、sh
の多くの行に対してcmd
の1行を記述できる場合は、次のようにすればよいでしょう。 $?
は:
を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 を使用して、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%
ニーズまたはコーディングスタイルに応じて、cmd
およびsh
コードをインターレースすることは意味がある場合とない場合があります。ヒアドキュメントの使用は、このようなインターレースを実行する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%
もちろん、普遍的なコメントは、文字列: #
または:;#
を使用して実行できます。 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%
したがって、私の知る限り、深刻な副作用なしに(およびsh
出力'#' is not recognized as an internal or external command, operable program or batch file.
なしで)cmd
およびcmd
互換スクリプトを実現するためのいくつかのアイデアと方法。
これを試すことができます:
#|| goto :batch_part
echo $PATH
#exiting the bash part
exit
:batch_part
echo %PATH%
おそらく、Unixスタイルの代わりに/r/n
を新しい行として使用する必要があります。覚えている場合、Unixの新しい行は、.bat
スクリプトによって新しい行として解釈されません。ここに私の答えと同様の方法で何もしないパスの#.exe
ファイル: 一時ファイルを使用せずにバッチファイル内にVBScriptを埋め込み、実行することは可能ですか?
[〜#〜] edit [〜#〜]
binki's answer はほぼ完璧ですが、それでも改善できます:
:<<BATCH
@echo off
echo %PATH%
exit /b
BATCH
echo $PATH
再度:
トリックと複数行コメントを使用します。cmd.exe(少なくともwindows10では)のような外観は、unixスタイルのEOLでは問題なく動作するため、スクリプトをLinux形式に変換してください。 ( here および here の前に同じアプローチが使用されていました)。 Shebangを使用しても冗長な出力が生成されますが...
コメントしたかったのですが、現時点では回答を追加することしかできません。
与えられたテクニックは優れており、私もそれらを使用しています。
2種類の改行を含むファイルを保持することは困難です。bash部分の/n
とwindows部分の/r/n
です。ほとんどのエディターは、編集しているファイルの種類を推測することにより、一般的な改行スキームを実行しようとします。また、インターネット経由でファイルを転送するほとんどの方法(特にテキストファイルまたはスクリプトファイルとして)は改行を洗浄するため、ある種類の改行から始めて別の種類の改行で終わる可能性があります。改行について想定してから、スクリプトを使用するために他の誰かにスクリプトを渡した場合、彼らにとってはうまくいかないかもしれません。
もう1つの問題は、異なるシステムタイプ間で共有されるネットワークマウントファイルシステム(またはCD)です(特に、ユーザーが利用できるソフトウェアを制御できない場合)。
したがって、/r/n
のDOS改行を使用し、各行の最後にコメントを入れることで/r
からbashスクリプトを保護する必要があります(#
)。また、/r
により改行されるため、bashで行継続を使用することもできません。
この方法で、スクリプトを使用する人は誰でも、どのような環境でも、スクリプトは機能します。
このメソッドは、ポータブルMakefileの作成と組み合わせて使用します!
上記の回答とは異なり、Bash 4およびWindows 10では、以下のエラーまたはエラーメッセージはありません。ファイルに「whatever.cmd」という名前を付けて、chmod +x
をLinuxで実行可能にし、Unixの行末文字(dos2unix
)bashを静かに保つため。
:; 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
同じスクリプトでbash
とcmd
で異なるコマンドを実行する方法はいくつかあります。
cmd
は、他の回答で述べたように、:;
で始まる行を無視します。また、rem ^
文字は改行をエスケープし、次の行はrem
によってコメントとして扱われるため、現在の行がコマンド^
で終わる場合、次の行も無視します。
bash
がcmd
行を無視するようにするには、複数の方法があります。 cmd
コマンドを壊さずにそれを行う方法をいくつか列挙しました。
#
コマンド(非推奨)スクリプトの実行時にcmd
で使用可能な#
コマンドがない場合、これを行うことができます。
# 2>nul & echo Hello cmd! & rem ^
echo 'Hello bash!' #
cmd
行の先頭にある#
文字は、bash
がその行をコメントとして扱うようにします。
ブライアントムプセットが his answer で指摘したように、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
で特別な意味を持たないため、echo
に対するテキストの一部として扱われます。必要なのは、echo
コマンドの出力をリダイレクトし、その後に他のコマンドを挿入することだけです。
#.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
上で空の#.bat
ファイルを作成し(または、存在する場合は既存の#.bat
を置き換えます)、bash
上では何もしません。
このファイルは、cmd
に他の#
コマンドがある場合でも、後続のPATH
行で使用されます。
cmd
固有のコードのdel #.bat
コマンドは、作成されたファイルを削除します。これは、最後の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
正確にhere-document delimiter
を含むcmd
行がない限り、このソリューションは機能します。 here-document delimiter
を他のテキストに変更できます。
提示されたすべてのソリューションでは、コマンドは最後の行の後にのみ実行され、同じことを行うと動作が一貫します両方のプラットフォーム。
これらのソリューションは、\r\n
を改行としてファイルに保存する必要があります。そうしないと、cmd
。で動作しません。
変数を共有できます:
:;SET() { eval $1; }
SET var=value
:;echo $var
:;exit
ECHO %var%
前の回答はほとんどすべてのオプションを網羅しているようで、私を大いに助けてくれました。ここでは、同じファイルにBashスクリプトとWindows CMDスクリプトの両方を含めるために使用したメカニズムを示すために、この回答を含めています。
_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.
_
最初の行(_echo >/dev/null # >nul & GOTO WINDOWS & rem ^
_)は無視され、_exit 0
_コマンドが実行されるまで、スクリプトはその直後の各行を流れます。 _exit 0
_に達すると、スクリプトの実行は終了し、その下のWindowsコマンドは無視されます。
最初の行は_GOTO WINDOWS
_コマンドを実行し、その直後のLinuxコマンドをスキップし、_:WINDOWS
_行で実行を継続します。
このファイルをWindowsで編集していたので、Linuxコマンドからキャリッジリターン(\ r)を体系的に削除する必要がありました。さもないと、Bash部分を実行したときに異常な結果になりました。これを行うために、 Notepad ++ でファイルを開き、以下:
行末文字を表示するオプションをオンにします(View
> _Show Symbol
_> _Show End of Line
_)。キャリッジリターンはCR
文字として表示されます。
検索と置換(Search
> _Replace...
_)を実行し、Extended (\n, \r, \t, \0, \x...)
オプションを確認します。
_\r
_フィールドに_Find what :
_と入力し、_Replace with :
_フィールドを空白にして、何も含まれないようにします。
ファイルの先頭から、Replace
ボタンをクリックして、すべてのキャリッジリターン(CR
)文字がLinuxの最上部から削除されるまでクリックします。 Windowsの部分には必ず復帰(CR
)文字を残してください。
結果として、各Linuxコマンドは改行(LF
)で終わり、各Windowsコマンドは復帰と改行(CR
LF
)で終わります。
この手法を使用して、実行可能な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
@
が/dev/null
にパイプされるエラーをスローし、その後コメントが開始されるためです。 cmdでは、/dev/null
へのパイプは失敗します。これは、ファイルがWindowsで認識されないためです。ただし、Windowsは#
をコメントとして検出しないため、エラーはnul
にパイプされます。その後、エコーがオフになります。行全体の前に@
が付いているため、cmdでprintetを取得しません。::
をshでnoopに定義します。これには、::
が$?
を0
にリセットしないという利点があります。 「:;
はラベルです」トリックを使用します。::
を追加でき、cmdでは無視されます:: exit
でshスクリプトが終了し、cmdコマンドを書くことができますcommand not found
を出力するため、cmdでは最初の行(Shebang)のみが問題になります。必要かどうかは自分で決める必要があります。