IT/기초 지식

[bat] Windows의 배치 파일(*.bat) 작성법

개발자 두더지 2022. 2. 4. 22:50
728x90
@echo off

call :TESTAAA 
exit /b 0

:TESTAAA
echo TESTAAA가 호출됐다.
exit /b 0

 배치 파일의 기본적인 작성법에 대해 알아보자.

 

bat실행시에 스크립트 내용을 명령 프롬프트에 표시하지 않도록 하기


 예를 들어 아래와 같은 배치 파일이 있다고 가정하자.

IF 1==1 (
    echo 가나다라
)

 이 파일을 실행시키면 아래와 같이 명령 프롬프트창에 표시된다.

C:>C:\exsample.bat

C:>IF 1 == 1 (echo 가나다라 )
가나다라

 이렇게 출력이 되지 않도록 하기 위해서는 배치 파일의 맨 앞에 반드시 @echo off를 정의해둘 필요가 있다.

@echo off
IF 1==1 (
    echo 가나다라
)

 다시 한 번 실행시키면, 아래와 같이 파일 내용이 표시되는 것을 방지할 수 있다.

C:>C:\exsample.bat
가나다라

 

 

 

코멘트(주석)


표준이라면 주석처리하기 위해섯 rem 커맨드를 사용한다.

@echo off
rem 주석입니다

 그러나 : 를 사용하는 방법도 있다. :는 GOTO와 함께 사용하는 라벨이지만, GOTO가 없으면 무시되무로 코멘트화하는 용도로도 사용할 수 있다.

@echo off
: 커맨드는 아니지만 주석입니다

 

 

 

스크립트가 저장되어 있는 장소를 현재 디렉토리로 하기


cd /d %~dp0

 모든 배치 파일의 맨 앞에 작성해도 좋은 라벨으로 자주 사용된다.

@echo off
cd /d %~dp0

 대부분 이 두줄이 기본 템플릿이 된다.

 

 

변수(정적 변수)


 변수는 환경변수로 사용된다. 또한 변수는 한 번 정의하면 변경할 수 없다. 변경할 수 있는 방법은 있긴 하지만, 여기서는 설명을 생략하도록 한다.

스크립트 실행중에만 유효한 환경 변수

스크립트 실행 중에만 유효한 환경 변수는 아래와 같이 정의할 수 있다.

set HENSU=AIUEO

변수의 내용을 참조하기

%를 활용한다. 아래는 예이다. 변수 "HENSU"를 정의하여 echo한다.

@echo off
set HENSU=AIUEO
echo %HENSU%

환경변수의 수명

스크립트 실행중만 유효한 환경변수를 다른 배치 파일에서 호출할 수 있다.

예를 들어 아래와 같이 2개의 배치 파일이 있다고 가정해보자.

: example.bat
@echo off
call TEIGI_AIUEO.bat
echo %HENSU%
: TEGI_AIUEO.bat
set HENSU_A=AIUEO

 여기서, example.bat을 실행하면 실행 결과는 아래와 같다.

AIUEO

 아직 해설하지 않은 커맨드인 "call"이 등장하는데, 이것은 다른 배치파일을 호출하는 커맨드이다. "call" 없이 그대로 "TEGI_AIUEO.bat"만을 작성해도 호출되지만, 호출한 배치 파일(이 경우는 example.bat)에 처리가 반환되지 않으므로 의도하지 않은 동작을 할 가능성이 있다.

 아무튼 이 와 같이 "TEGI_AIUEO.bat"에 정의된 변수가 "example.bat"에서 부터 참조할 수 있다.

 

 

if문


구문

if 조건 ( 처리 )

코딩의 일반적인 문법과 거의 동일하다. 뒤에서 설명하겠지만 "()"은 부분은 아래와 같이 행을 나눠서 작성하는 것이 좋을 것 같다.

if 조건 (
    처리1
    처리2
    처리3
) else (
    처리1
    처리2
    처리3
)

 처리가 1행뿐일 경우 괄호는 다음과 같이 생략해도 상관없다.

if 조건 처리

조건식 : 문자열 검사

환경 변수 "Configuration"이 Debug로 되어 있는 경우 등 VC의 빌드 이벤트에 종종 사용된다.

@echo off
set Configuration=Debug
if %Configuration%==Debug (
    echo 디버그빌드
) else (
    echo 릴리즈빌드
)
[ 실행결과 ]
디버그빌드

조건식: 파일의 존재

if의 뒤에 exist를 붙인다.

@echo off
cd /d %~dp0
if exist exsample.bat (
    echo exsample.bat가 현재 디렉토리에 존재한다
) else (
    echo exsample.bat가 현재 디렉토리에 존재하지 않는다
)
[ 실행결과 ]
exsample.bat가 현재 디렉토리에 존재한다

조건식 : 부정

if의 뒤에 not을 붙인다.

@echo off
set Configuration=Debug
if not %Configuration%==Debug (
    echo 릴리즈빌드
) else (
    echo 디버그빌드
)
[ 실행결과 ]
디버그빌드

if exist의 경우도 not을 if의 바로 뒤에 붙이면 좋다.

exist이외에도 다양하게 있지만, it의 직후에 not을 붙이는 것이 일반적이다.

@echo off
cd /d %~dp0
if not exist exsample.bat (
    echo exsample.bat가 현재 디렉토리에 존재하지 않는다
) else (
    echo exsample.bat가 현재 디렉토리에 존재한다
)

 

 

 

코딩 규약


대문자인가 소문자인가

개발자가 여러명이라면 일치하도록 정하고, 혼자라면 어떤 문자든 상관없다.

괄호

if와 for의 괄호만은 행을 바꿔서 작성하는 편이 좋다고 생각한다. if라면 괜찮을지라도 for문의 경우 읽기 힘들어진다. 앞서 말했듯 조건이 하나일 경우 괄호를 생략해도 좋지만 적어주는 편이 가독성이 좋다.

@echo off
IF 1==1 (
    echo 커맨드
    echo 커맨드
    echo 커맨드
    IF 1==1 (
        echo IF안에 IF가 들어있어도 읽기 편리하다
        IF 1==1 (
            echo 커맨드
            IF 1==1 (
                rem 커맨드
            )
        )
    )
)

 

 

 

다른 배치 파일의 호출


호출

앞서 봤듯, call을 사용한다. 이 외에 다른 방법은 비동기 호출이 되므로 좋지 않다.

call 호출할배치파일.bat 인수1 인수2 인수3 ...

배치의 종료

exit를 사용한다.

반환 값을 return

return이 아닌 exit를 사용한다.

exit 0

문자열은 반환되지 않으므로, 문자열을 반환하고 싶을 경우 별도의 변수를 정의해야한다.

@echo off
rem 반환값을 정의
RETURN_VALUE=AIUEO
exit /b 0

배치 파일만 종료하기

exit는 cmd.exe 그 자체를 종료하는 커맨드이므로, 기본적으로 이대로 잘 사용하지 않는다고 생각된다. 예를 들어, call으로 호출될 것을 전제로 한 배치 파일이라면, 호출한 배치 파일까지 종료시키면 안되므로, 하나의 배치 파일만을 종료시킬 필요가 있다.

 이 경우 "/b" 옵션을 지정한다.

exit /b 0

호출된 배치의 반환 값을 확인하기

ERRORLEVEL환경 변수를 살펴보자.

call test.bat
if %errorlevel% == 1 (
    echo test.bat를 실행한 결과, 반환값으로써 1가 반환됐다
)

"if errorlevel 1"로 하면 "ERRORLEVEL환경 변수가 1이상의 경우"라는 의미가 되므로, 의도와 달리 작동하게 되는 경우가 많다.

call test.bat
if errorlevel 0 (          ※1
    echo test.bat를 실행한 결과, 반환값으로써 0이 반환됐다
)
if errorlevel 1 (          ※2
    echo test.bat를 실행한 결과, 반환값으로 1이 반환됐다
)

 위 코드로 반환값으로 1이 반환되어도 실제로는 "0이상", "1이상' 양쪽의 조건을 둘다 충족되기 때문에, ※1과 ※2읭 양쪽 조건 분기가 실행된다.

 

 

함수의 호출


 배치 파일에는 함수 자체가 없다. 대신에 GOTO를 사용한다.

라벨의 정의

: 로 정의한다.

:OreOreRaberu

라벨로 이동

@echo off
call :OreOreRaberu
echo AIUEO

:OreOreRaberu
echo OREORE
exit /b
[실행결과]
OREORE
AIUEO
OREORE

 마지막의 OREOR은 왜 출력되어 버린 것 일까? 이것은 제대로 exit하지 않았기 때문이다.

@echo off
call :OreOreRaberu
echo AIUEO
exit /b

:OreOreRaberu
echo OREORE
exit /b
[실행결과]
OREORE
AIUEO

인수

call시에 인수를 전달한다.

함수 내에는 전달된 인수의 순서대로 %1, %2, %3...으로 할당된다.

@echo off
call :OreOreRaberu HIKISU1 HIKISU2 HIKISU3
exit /b

:OreOreRaberu
echo %1
echo %2
echo %3
exit /b
[실행결과]
HIKISU1
HIKISU2
HIKISU3

 

 

동적 변수


동적 변수의 정식 명칭은 "지연 환경 변수"이다. 동적 변수를 사용하는 이유는 무엇일까? set에 의해 환경 변수를 설정하는 것은 커맨드 프롬프트의 실행 단위에 따라 예상한 대로 동작하지 않는 경우가 있다. 예를 아래와 같은 배치 파일이 있다고 가정하자.

@echo off
setlocal enabledelayedexpansion
if 1==1 (
    set TEST=AIUEO
    echo %TEST%

    set TEST=KAIKUKEKO
    echo %TEST%
)

 실행시키면, 아래와 같은 결과가 출력된다.

[실행결과]
ECHO 는 입니다.
ECHO 는 입니다.

"ECHO는 입니다."는 echo에 출력될 것이 없는 경우에 표시된다. 즉. TEST 변수에는 아무것도 정의되어 있지 않은 상태이다. 명령어는 한 줄씩 해석되는데 대입이 발생하면 그 타이밍에 대입한 값이 출력되는 동작이 되지 않는 경우가 많다. 또한 마지막에 echo를 하나 더 추가하면 KAIKUKEKO가 표시된다.

@echo off
setlocal enabledelayedexpansion
if 1==1 (
    set TEST=AIUEO
    echo %TEST%

    set TEST=KAIKUKEKO
    echo %TEST%
)

echo %TEST%
[실행결과]
ECHO 는 입니다.
ECHO 는 입니다.
KAKIKUKEKO

 

생각한대로 변수의 정의와 값 변경하는 지연 환경 변수를 정의하기

@echo off
setlocal enabledelayedexpansion
if 1==1 (
    set TEST=AIUEO
    echo !TEST!

    set TEST=KAIKUKEKO
    echo !TEST!
)
[실행결과]
AIUEO
KAKIKUKEKO

사용법

setlocal enabledelayedexpansion

를 맨 앞에 기재한다. 그럼 아래와 같이 배치 파일의 맨 앞이 정의된다.

@echo off
cd /d %~dp0
setlocal enabledelayedexpansion

지연 환경 변수 : 대입

set TEST=AIUEO

특별히 다른점은 없다.

지연 환경 변수 : 참을 참고하기

set TEST=AIUEO
!TEST!

보통의 환경 변수는 %를 사용하지면 지연 환경 변수는 !를 사용한다.

 

 

파일 패스의 조작


개요

파일 패스가 정의되어 있는 환경변수로 부터 파일명등을 다룰 때 사용할 수 있다.

커맨드

%~nx변수명

그런데 아래와 같이 그대로 스크립트를 작성하면 구문 에러가 발생한다.

@echo off

set FILE_PATH=C:/aaa/bbb.txt
set FILE_NAME=%~nxFILE_PATH%

echo %FILE_PATH%
echo %FILE_NAME%

따라서 인수로 사용할 수 있다는 점을 이용해 다음과 같은 함수를 만든다.

:basename 
set RESULT_FILENAME=%~nx1

이는 전달 받은 인수의 파일명을 "RESULT_FILENAME"에 넣는 함수이다. 이것을 이용해 파일 패스를 반환시켜 지연 환경 변수로 RESULT_FILENAME에 액세스하면 파일명을 취득할 수 있다.

 

 

for


 

구문

for %%1문자의변수명 in (패턴) do (
   처리1 
   처리2
   처리3
)

동작

일반적으로는 변수명으로 i를 사용한다. 커맨드 프롬프트의 for은 디폴트 동작으로써 현재 디렉토리에 존재하는 파일 전체를 모두 열거한다.

 즉, 다음과 같이하면 배치 파일에 존재하는 디렉토리 안의 모든 파일명을 echo한다.

@echo off
cd /d %~dp0

for %%i in (*) do (
    echo %%i
)

 

 

goto와 call


개요

goto와 call은 꽤 비슷한 커맨드이다. 둘 다 라벨로 이동한다는 점은 동일하나, 몇 가지 동작상의 차이점이 있다.

인수

goto에서는 인수를 전달하지 않지만, call은 전달한다.

call의 경우 라벨로 이동할 때에 콜론이 필요

call은 원래 외부의 배치 파일이나 exe를 호출하기 위해 사용되므로, 배치 파일 내부의 라벨로 이동하는 경우에는 콜론을 붙이지 않으면, 라벨인지 exe를 실행하고 싶은지 판단할 수 없다.

@echo off

call :TESTAAA 
exit /b 0

:TESTAAA
echo TESTAAA가 호출됐다.
exit /b 0
[실행결과]
TESTAAA가 호출됐다.

 위의 예와 같이 "call:TESTAAA"이라는 라벨명에 콜론을 붙일 필요가 있다. 여기서 "call TESTAAA"와 같이 작성하면, "TESAAA이라는 커맨드를 발견하지 못 했습니다."라는 에러가 발생한다.

 그와 반면, goto의 경우 원래 용도가 "다른 라벨로의 이동"이므로 콜론을 붙일 필요가 없다.

@echo off

goto TESTAAA 
exit /b 0

:TESTAAA
echo TESTAAA가 호출됐다.
exit /b 0
[실행결과]
TESTAAA가 호출됐다.

exit때의 동작과 차이점

 보통 옵션이 없는 exit, 즉 "exit 0"의 경우, goto의 경우도 call의 경우도 즉각 cmd.exe를 종료시킨다. 그러나 "/b"옵션을 추가한 경우 두 커맨드의 동작이 조금 달라진다.

 call의 경우 서브 루틴으로 빠지는 동작이된다.

@echo off

call :TESTAAA 
echo TESTAAA로부터 돌아왔다.
exit /b 0

:TESTAAA
echo TESTAAA가 호출됐다.
exit /b 0
[실행결과]
TESTAAA가 호출됐다.
TESTAAA로부터 돌아왔다.

 그러나 goto의 경우 돌아오지 않는다.

@echo off

call :TESTAAA 
echo TESTAAA로부터 돌아왔다.
exit /b 0

:TESTAAA
echo TESTAAA가 호출됐다.
exit /b 0
[실행결과]
TESTAAA가 호출됐다.

 이는 goto가 어디까지나 호출이 아닌 단순 실행 행으로의 이동일 뿐임을 나타낸다.

 

 

setlocal, endlocal


 배치 파일 A, 배치 파일B 두 가지가 존재할 때, 배치 파일 A에서 배치 파일 B를 호출하면, 환경 변수가 set된 배치 파일 에도 그 환경 변수를 참조할 수 있다.

 그렇게 하기 싫은 경우, 즉 배치 파일 B에 정의된 환경 변수를 그 배치파일 내에서만 사용하고 싶을 경우에 setlocal과 endlocal을 사용한다.

@echo off

setlocal
    rem setlocal、endlocal사이에 있는 환경변수는 그 안에서만 유효하다.
endlocal

참고자료

https://qiita.com/sksmnagisa/items/8c4c1788af44cc1dc63a

728x90