[bash] Windows 배치와 Linux Bash에서 실행되는 단일 스크립트?

Windows (.bat로 처리)와 Linux (Bash를 통해) 모두에서 실행되는 단일 스크립트 파일을 작성할 수 있습니까?

나는 둘 다의 기본 구문을 알고 있지만 이해하지 못했습니다. Bash의 모호한 구문이나 Windows 배치 프로세서 결함을 악용 할 수 있습니다.

실행할 명령은 다른 스크립트를 실행하는 한 줄일 수 있습니다.

동기 부여는 Windows와 Linux 모두에 대해 단일 응용 프로그램 부팅 명령을 갖는 것입니다.

업데이트 : 시스템의 “네이티브”쉘 스크립트에 대한 필요성은 올바른 인터프리터 버전을 선택하고 특정 잘 알려진 환경 변수를 준수해야한다는 것입니다. CygWin과 같은 추가 환경을 설치하는 것은 바람직하지 않습니다. 개념을 유지하고 싶습니다. ” 다운로드 및 실행 “.

Windows에서 고려해야 할 유일한 다른 언어는 Windows Scripting Host-WSH이며, 이는 98 이후 기본적으로 사전 설정되어 있습니다.



답변

내가 한 일은 cmd의 레이블 구문을 주석 마커로 사용하는 것 입니다. 레이블 문자 인 콜론 ( :) true은 대부분의 POSIX 쉘에서 와 동일합니다 . 에서 사용할 수없는 다른 문자가 레이블 문자 바로 뒤에 오는 경우 스크립트에 GOTO주석을 달아도 코드에 cmd영향을주지 않습니다 cmd.

해킹은 문자 시퀀스 ” :;” 뒤에 코드 줄을 넣는 것 입니다. 대부분 한 줄짜리 스크립트를 작성하거나 경우 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코드를 사용하는 것입니다 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%

필요 나 코딩 스타일에 따라 인터레이스 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간주 되기 때문에 필요 #합니다. 예를 들어 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%

따라서 내가 아는 한 심각한 부작용없이 (그리고 출력 없이 ) 스크립트 를 완성 sh하고 cmd호환 되는 몇 가지 아이디어와 방법 .cmd'#' is not recognized as an internal or external command, operable program or batch file.


답변

당신은 이것을 시도 할 수 있습니다 :

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

아마도 /r/n유닉스 스타일 대신 새 줄로 사용해야 할 것 입니다. 정확한 것을 기억한다면 유닉스 줄 바꿈은 스크립트에 의해 새 줄로 해석되지 않습니다 .bat. 또 다른 방법은 #.exe경로에 아무것도하지 않는 파일 을 만드는 것입니다. 내 대답과 비슷한 방식으로 임시 파일을 사용하지 않고 배치 파일 내에 VBScript를 포함하고 실행할 수 있습니까?

편집하다

binki의 대답은 거의 완벽하지만 여전히 개선 될 수있다 :

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

echo $PATH

그것은 다시 :트릭과 여러 줄 주석을 사용합니다. cmd.exe (적어도 windows10에서는)가 유닉스 스타일 EOL에 문제없이 작동하므로 스크립트가 Linux 형식으로 변환되는지 확인하십시오. (이전에 여기여기에 동일한 접근 방식이 사용되었습니다 ). shebang을 사용하면 여전히 중복 출력이 생성되지만 …


답변

댓글을 달고 싶었지만 지금은 답변 만 추가 할 수 있습니다.

주어진 기술은 훌륭하고 나도 사용합니다.

/nbash 부분과 /r/n창 부분에 대한 두 가지 종류의 줄 바꿈이 포함 된 파일을 유지하는 것은 어렵습니다 . 대부분의 편집자는 편집중인 파일의 종류를 추측하여 일반적인 줄 바꿈 체계를 적용하려고합니다. 또한 인터넷을 통해 파일을 전송하는 대부분의 방법 (특히 텍스트 또는 스크립트 파일)은 줄 바꿈을 세탁하므로 한 종류의 줄 바꿈으로 시작하여 다른 종류로 끝낼 수 있습니다. 줄 바꿈에 대한 가정을 한 다음 다른 사람에게 스크립트를 제공하면 해당 스크립트가 작동하지 않을 수 있습니다.

다른 문제는 서로 다른 시스템 유형 (특히 사용자가 사용할 수있는 소프트웨어를 제어 할 수없는 경우)간에 공유되는 네트워크 마운트 파일 시스템 (또는 CD)입니다.

따라서 DOS 줄 바꿈을 사용해야하며 각 줄 끝에 주석을 추가하여 /r/nDOS에서 bash 스크립트를 보호해야합니다 /r( #). 또한 bash에서 줄 연속을 사용할 수 없습니다 /r.

이런 식으로 스크립트를 사용하는 사람과 어떤 환경에서든 작동합니다.

이 방법은 휴대용 Makefile을 만드는 것과 함께 사용합니다!


답변

다음은 위의 답변과 달리 Bash 4 및 Windows 10에서 오류 또는 오류 메시지없이 저에게 효과적입니다. 파일 이름을 “whatever.cmd”로 지정하고 chmod +xLinux에서 실행 가능하게 만들고 dos2unixbash를 조용하게 유지하기 위해 유닉스 줄 끝 ( )을 사용하도록합니다.

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


답변

변수를 공유 할 수 있습니다.

:;SET() { eval $1; }

SET var=value

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


답변

에 다른 명령을 실행하는 방법은 여러 가지가 있습니다 bashcmd같은 스크립트는.

cmd:;다른 답변에서 언급했듯이으로 시작하는 줄을 무시 합니다. 또한 현재 줄이 명령으로 끝나면 다음 줄을 무시합니다 rem ^. ^문자는 줄 바꿈을 이스케이프하고 다음 줄은 주석으로 처리됩니다 rem.

선 을 bash무시하는 cmd방법에는 여러 가지가 있습니다. cmd명령 을 중단하지 않고이를 수행하는 몇 가지 방법을 열거했습니다 .

존재하지 않는 #명령 (권장되지 않음)

스크립트가 실행될 때 #사용할 수있는 명령 이없는 경우 다음 cmd을 수행 할 수 있습니다.

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

#의 시작 부분에 캐릭터 cmd라인 차종은 bash주석으로 그 라인을 취급합니다.

#의 끝에 문자 bash라인은 주석하는 데 사용됩니다 \r브라이언 Tompsett는 지적으로, 문자를 그의 대답 . 이것이 없으면 bash파일에 \r\n에서 요구하는 줄 끝 이 있으면 오류가 발생합니다 cmd.

를 수행함으로써 # 2>nul우리는 cmd존재하지 않는 일부 #명령 의 오류를 무시하는 동시에 다음 명령을 계속 실행합니다.

에서 #사용할 수 있는 명령이 PATH있거나에서 사용할 수 있는 명령을 제어 할 수없는 경우이 솔루션을 사용하지 마십시오 cmd.


문자 echo무시에 사용#cmd

의 주석 처리 된 영역 에 명령 echo을 삽입하기 위해 리디렉션 된 출력과 함께 사용할 수 있습니다 .cmdbash

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줄은 #.baton 동안 빈 파일을 만들고 cmd(또는 기존 #.bat,있는 경우 대체 ) on 동안 아무 작업도 수행하지 않습니다 bash.

이 파일은에 cmd다른 #명령 이 있더라도 다음 행에서 사용됩니다 PATH.

특정 코드 의 del #.bat명령 cmd은 생성 된 파일을 삭제합니다. 마지막 cmd줄 에서만이 작업을 수행하면됩니다 .

#.bat파일이 현재 작업 디렉토리에있을 수있는 경우이 솔루션을 사용하지 마십시오 . 해당 파일이 지워집니다.


권장 : here-document 를 사용하여 cmd명령 무시bash

:; 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한이 솔루션이 작동합니다. here-document delimiter다른 텍스트로 변경할 수 있습니다 .


제시된 모든 솔루션 에서 명령은 마지막 줄 이후에만 실행 되므로 두 플랫폼에서 동일한 작업을 수행하면 동작이 일관되게됩니다.

이러한 솔루션은 \r\n줄 바꿈 으로 파일에 저장해야합니다 . 그렇지 않으면에서 작동하지 않습니다 cmd.


답변

이전 답변은 거의 모든 옵션을 다루고 많은 도움이되었습니다. 이 답변은 동일한 파일에 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에서

첫 번째 줄은 GOTO WINDOWS명령 을 실행하고 바로 뒤 따르는 Linux 명령을 건너 뛰고 해당 :WINDOWS줄 에서 계속 실행 합니다.

Windows에서 캐리지 리턴 제거

Windows에서이 파일을 편집하고 있었기 때문에 Linux 명령에서 캐리지 리턴 (\ r)을 체계적으로 제거해야했습니다. 그렇지 않으면 Bash 부분을 실행할 때 비정상적인 결과를 얻었습니다. 이를 위해 메모장 ++ 에서 파일을 열고 다음을 수행했습니다.

  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. 파일의 맨 위에서 시작 하여 맨 위 Linux 부분에서 Replace모든 캐리지 리턴 ( CR) 문자가 제거 될 때까지 단추를 클릭하십시오 . CRWindows 부분 에는 캐리지 리턴 ( ) 문자 를 그대로 두십시오 .

그 결과 각 Linux 명령은 줄 바꿈 ( LF)으로 끝나고 각 Windows 명령은 캐리지 리턴과 줄 바꿈 ( CR LF)으로 끝납니다 .