윈도우 명령 줄 스크립트

시작하기

우선 간단한 명령줄 배치 파일을 살펴보자.

@echo off
rem This is a comment!
echo Hi

@echo off는 거의 모든 명령줄 스크립트에 쓰며 보통 첫번째 줄에 놓는다. @echo off 가 없다면 아래와 같이 명령어 실행 과정을 일일이 보여준다. 보통은 사용자가 원치 않는 행동이다.

c:>a.bat

c:>rem This is a comment!

c:>echo Hi
Hi

rem은 주석 지시자로 생각하면 된다. C++의 // 와 동일한 역할을 한다.

ERRORLEVEL

윈도우 응용프로그램처럼 명령줄 배치 파일에도 종료 코드(exit code)가 있다. C++ 프로그램에선 main 함수의 반환값인 종료 코드를 명령줄 배치에선 오류 수준(ERRORLEVEL)이라 부른다. 일반적인 관례에 따르면 종료 코드 0은 성공을 의미하고 나머지 값은 실패를 뜻한다.

ERRORLEVEL 설정하기

명령줄 배치 스크립트에서 종료 코드를 명시적으로 반환할 땐 exit 명령어를 쓴다.

@echo off
rem This is a comment!
echo Hi
exit /b 0

/b 옵션이 있으면 배치 스크립트를 종료한다. 그렇지 않으면 아예 CMD.exe 프로세스를 종료시키므로 주의한다.

주의! 윈도우 버전에 따라 종료 코드를 반환하는 방식이 다른데 exit /b는 윈도우 2000 부터 도입됐다. 그 이전 버전에 대해 알고 싶다면 Errorlevels를 참고하자.

ERRORLEVEL 확인하기

배치 파일의 성공 여부를 알고자 한다면 명령줄에 다음과 같이 입력한다.

c:>a.bat
Hi!

c:>echo %errorlevel%
0

인자 넘겨받기

인자는 그 순서대로 %0, %1, %2 식으로 지칭하면 된다. 단 %0은 배치 파일의 이름을 나타내는 특수 인자임을 주의한다.

@echo off
echo filename = %0
xcopy /E %1 %2
exit /b 0

이 스크립트는 자신의 이름을 메시지로 내보낸 후 첫 번째 인자로 받은 디렉터리 트리를 두 번째 인자로 받은 경로에 복사한다.

c:>a.bat a a1
filename = a.bat
0 File(s) copied

마지막으로 특별한 인자 지시자 "%"를 소개한다. "%"는 모든 인자를 묶은 문자열을 나타낸다. 위의 코드에선 "a a1"이 된다.

사용자 입력 받기

표준입력장치에서 사용자의 입력을 받아 출력하는 예제를 보면 이해하기 쉽다.

@echo off
rem This is a comment!
set /p name=What is your name? 
echo Your name is %name%.
exit /b 0

이 배치 스크립트를 실행하면 다음과 같은 결과를 얻는다.

c:>a.bat
What is your name? Jay
Your name is Jay.

첫 번째 Jay는 사용자가 키보드로 입력한 값이다.

변수

SET

SET 명령을 사용하면 변수 선언이 가능하다.

@echo off
rem this is "conditional.bat".
set var1=first variable
echo %var1%

exit /b 0

이 방법의 특징은 스크립트가 종료된 후에도 선언한 변수는 유효하다는 것이다. CMD.exe가 종료되기 전까진 유효하다.

F:WorkspaceWinBatch>conditional.bat
first variable

F:WorkspaceWinBatch>set var1
var1=first variable

스크립트 내에서만 유효한 변수

위의 방법과 달리 스크립트 종료 후에 변수 선언을 무효로 만들려면 setlocal을 이용한다.

@echo off
rem this is "conditional.bat".

setlocal
set var1=first variable
echo in the local: %var1%
endlocal

echo out of the local: %var1%

exit /b 0
F:WorkspaceWinBatch>conditional.bat
in the local: first variable
out of the local:

SETX

SET 로 만들거나 값을 변경한 변수는 명령 줄 프로세스(CMD.exe)가 살아있는 동안만 유효하다. 이러한 제한을 벗어나려면 사용자/시스템 환경 변수를 직접 조작하는 수밖에 없다. SETX 는 이런 기능을 제공한다.

시스템 PATH 값을 조작하기

/M 스위치가 있으면 시스템 환경 변수를 바꾸고, 그렇지 않으면 사용자 환경 변수를 변경한다.

SETX PATH "%PATH%;C:Temp" /M


PATH 값을 조작할 때는 경로 끝에  이 붙지 않게 한다. 그래야 오류가 나지 않는다. 다음과 같은 명령은 재앙을 일으킬 수도 있다.

SETX PATH "%PATH%;C:Temp" /M


#### 특수 변수

##### %RANDOM%

0 ~ 32767 사이의 값을 무작위로 뽑아 반환한다. 
c:>echo %RANDOM%
30401
%CD%

현재 작업 디렉터리를 나타낸다.

@echo off
rem D:test.bat 

echo %CD%

C:에서 D:에 있는 test.bat 파일을 실행하면 다음과 같은 결과가 나온다.

C:>D:test.bat
C:
%0 외

%0 계열의 변수는 작업 디렉터리가 하닌 배치 스크립트의 경로를 기준으로 값을 정한다.

UNC 경로일 때
%0 = "\\serverusersmystuffTempMy TestMy Batch File.cmd"
%~0 = \\serverusersmystuffTempMy TestMy Batch File.cmd
%~d0 = \\
%~p0 = serverusersmystuffTempMy Test
%~dp0 = \\serverusersmystuffTempMy Test
드라이브 경로일 때
%0 = "H:TempMy TestMy Batch File.cmd"
%~0 = H:TempMy TestMy Batch File.cmd
%~d0 = H:
%~p0 = TempMy Test
%~dp0 = H:TempMy Test
%DATE% 과 %TIME%

각각 오늘 날짜와 현재 시간을 반환한다.

c:>echo %DATE% %TIME%
2009-10-06 17:56:50.53

서버의 언어 설정에 따라선 포맷이 다르기도 하다.

c:>echo %DATE% %TIME%
Tue 10/06/2009 17:56:50.53

그러므로 %DATE%를 파싱할 땐 특히 주의해야 한다. 아래 코드는 후자(지역 언어: 영어)에서만 제대로 작동한다.

@echo off 
echo %DATE% %TIME%
set MTH=%DATE:~4,2%
set DAY=%DATE:~7,2%
set YR=%DATE:~10,4%
set HR=%TIME:~0,2%
set HR0=%TIME:~0,1%
if "%HR0%"==" " set HR=0%TIME:~1,1%
set MIN=%TIME:~3,2%
set SEC=%TIME:~6,2%
set MYDATE=%YR%%MTH%%DAY%-%HR%%MIN%%SEC%
echo %MYDATE%
그밖의

마이크로소프트 사가 제공하는 Command shell overview 문서를 참고한다.

조건문

if exist 파일이름

파일의 존재 여부를 확인할 때 사용한다.

@echo off
rem this is "conditional.bat".
if exist conditional.bat echo there it is!
if not exist no.bat echo it doesn't exist!
exit /b 0

이 배치 스크립트의 결과는 다음과 같다.

F:WorkspaceWinBatch>conditional.bat
there it is!
it doesn\'t exist!

if defined

변수의 존재 여부를 확인할 때 사용한다.

@echo off
rem this is "conditional.bat".
setlocal
set var1=first variable
if defined var1 echo val1 is there!
if not defined var2 echo val2 is not there!
endlocal 

exit /b 0

이 배치 스크립트의 결과는 다음과 같다.

F:WorkspaceWinBatch>conditional.bat
val1 is there!
val2 is not there!

if 문자열1==문자열2

@echo off
setlocal
set var1=variable
set var2=variable
set var3=some-variable
if %var1% == %var2% (echo var1 equals to var2) else (echo var1 not equals to var2)
if %var1% == %var3% (echo var1 equals to var3) else (echo var1 not equals to var3)
endlocal 
 
exit /b 0

이 배치 스크립트의 결과는 다음과 같다.

F:WorkspaceWinBatch>test.bat
var1 equals to var2
echo var1 not equals to var3
비교 연산자
  • EQU – equal
  • NEQ – not equal
  • LSS – less than
  • LEQ – less than or equal
  • GTR – greater than
  • GEQ – greater than or equal
대소문자 구분

기본적으로 문자열 비교시 대소문자 구분 을 한다. 만약 대소문자의 구분 없이 비교하고 싶다면 /I 옵션을 주면 된다.

@echo off
setlocal
set var1=variable
set var2=variable
set var3=VARIABLE
if %var1% EQU %var2% (echo var1 equals to var2) else (echo var1 not equals to var2)
if %var1% EQU %var3% (echo var1 equals to var3) else (echo var1 not equals to var3)
if /I %var1% EQU %var3% (echo var1 equals to var3) else (echo var1 not equals to var3)
endlocal 
 
exit /b 0

이 배치 스크립트의 결과는 다음과 같다.

F:WorkspaceWinBatch>test.bat
var1 equals to var2
echo var1 not equals to var3
var1 equals to var3

if errorlevel

{{미완성}}

for 를 이용한 순회

주의! 변수 이름은 대소문자를 구분한다!

문자열, 명령 출력값 파싱하기

/f 옵션을 쓰면 파일, 문자열, 명령 출력값에서 필요한 텍스트를 검사하고 찾아낼 수 있다. 이를테면,

for /f %%a in ('cd') do set MYROOT=%%a

이 배치 파일은 명령줄에서 cd를 실행시키고 그 결과(현재 디렉터리의 경로) %%a 변수에 저장한다. 그런 후 MYROOT라는 환경변수에 현재 디렉터리의 경로를 입력한다.

참고 문헌

*Microsoft Windows XP – For

문자열 다루기

문자열 추출

문자열의 인덱스 값을 지정해 부분 문자열을 뽑아낼 수 있다. 대부분의 부분 문자열 메서드와 마찬가지로 첫 매개변수는 시작 지점이고 두 번째 매개변수는 문자열의 길이이다.

예제 1
@echo off

set str=politic
echo.%str%
set str=%str:~0,4%
echo.%str%
politic
poli
예제 2
@echo off

echo.Date   : %date%
echo.Weekday: %date:~0,3%
echo.Month  : %date:~4,2%
echo.Day    : %date:~7,2%
echo.Year   : %date:~10,4%
Date   : Sat 03/11/2006
Weekday: Sat
Month  : 03
Day    : 11
Year   : 2006
예제 3
@echo off

set str=politic
echo.%str%
set str=%str:~1,-1%
echo.%str%
politic
oliti
예제 4
set str=politic
echo.%str%
set str=%str:~-4%
echo.%str%
politic
itic

참고 문헌

고급 기법

래퍼 파일

만약 msbuild.exe를 쓸 생각이라면 어떨까? 닷넷 프레임워크의 버전마다 msbuild.exe 바이너리가 따로 있으며 그 경로는 매우 복잡하다. 이런 경우엔 다음과 같이 배치 파일을 만들면 된다.

@echo off
"C:WINDOWSMicrosoft.NETFramework64v3.5msbuild.exe" %*

이게 끝은 아니다. msbuild.exe의 종료 코드를 받아 전달해야 완전한 래퍼 파일이 된다.

rem NewMSBuild.bat
@echo off

C:WINDOWSMicrosoft.NETFramework64v3.5msbuild.exe %*
exit /b %errorlevel%

이보다 복잡한 스크립트라면 고려할 점이 또 있다. 배치 스크립트에선 환경변수를 조작하는 일이 흔하다. 그러나 스크립트 종료 후에 환경 설정을 원래대로 돌리고 싶은 게 보통이다. 여러 방법이 있지만 가장 간단한 방법만 보자.

rem MyMSBuild.bat
@echo off

CMD.exe /K NewMSBuild.bat %*
SET ERR_LEVEL=%errorlevel%
exit /b %ERR_LEVEL%

이 예제는 환경 변수를 조작하는 코드는 모두 NewMSBuild.bat에 있다고 가정한다. 새 인터프리터를 띄우고 그 안에서 환경 변수를 조작한다. 스크립트를 다 실행한 후 인터프리터가 종료되고 그와 동시에 스크립트 안에서 조작한 환경 변수는 모두 날아간다.

현재 디렉터리 #1

현재 디렉터리를 구하는 방법은 다음과 같다.

@echo off

rem little complicated 
SETLOCAL
for /f %%a in (\'cd\') do set MYROOT=%%a
echo %MYROOT%
ENDLOCAL

rem more easiler way
echo %CD%

두 번째는 시스템이 제공하는 의사 변수인 %CD%를 사용한다. %CD%가 없는 시스템에선 첫 번째 방법처럼 CD 명령을 실행시켜 그 출력값을 가져오면 된다.

현재 디렉터리 #2

CD 명령이나 %CD% 의사 변수를 사용하는 방법엔 문제가 있다.

@echo off
rem F:WorkspaceWinBatchdirshow.bat

echo this is %%cd%%  %cd%
echo this is %%~dp0 %~dp0

F:WorkspaceWinBatch 폴더에 dirshow.bat 란 배치 파일이 있다고 하자. dirshow.bat를 F:WorkspaceWinBatch에서 실행하면 예상했던 출력값이 나온다.

F:WorkspaceWinBatch>dirshow.bat

F:WorkspaceWinBatch>
this is %cd%  F:WorkspaceWinBatch
this is %~dp0 F:WorkspaceWinBatch

그러나 C:에서 dirshow.bat를 실행시키면 다른 결과가 나온다.

c:>F:WorkspaceWinBatchdirshow.bat
this is %cd%  c:
this is %~dp0 F:WorkspaceWinBatch

CD는 명령을 실행한 경로를 반환한다. 그에 반해 %~dp0%은 배치 스크립트 파일의 경로를 반환한다. 상황에 맞는 방법을 선택하면 된다.

부모 디렉터리

제일 쉬운 방법은 %~dp0% 를 이용하는 것이다.

@echo off
rem D:WorkspaceWinBatchdirshow.bat

echo this is %%~dp0 %~dp0..

이 방법의 단점은 경로가 지저분하다는 점이다.

D:WorkspaceWinBatch..

조금 더 깔끔한 경로를 원한다면 이렇게 한다.

@echo off
rem F:WorkspaceWinBatchdirshow.bat

for %%? in ("%~dp0..") do echo %%~f?
D:Workspace 
Advertisements

최 재훈

블로그, 페이스북, 트위터 고성능 서버 엔진, 데이터베이스, 지속적인 통합 등 다양한 주제에 관심이 많다.
Close Menu