SharedIT | 묻고 답하기(AMP)

하위폴더->상위폴더 파일 옮기기 배치파일 질문입니다

@echo off

setlocal

cd /d "%~dp0"

set "z_path=D:\test2\테스트1"

set "z_ext=flac"

set "z_file=*"



:: 폴더 리스트

for /f "usebackq delims=" %%f in (`dir /b /ad "%z_path%"`) do (

echo "%z_path%\%%~nxf"

call set /a z_num=0



for /f "usebackq delims=" %%g in (`dir /b /s "%z_path%\%%~nxf\%z_file%.%z_ext%"`) do (

call set /a z_num=%%z_num%%+1

call set z_numm=00000%%z_num%%

call set z_numm=%%z_numm:~-4%%

call move "%%g" "%z_path%\%%~nxf\%%z_numm%%.%z_ext%"

ping localhost -n 1 >nul

rd /q "%%~dpg" >nul

)

)



endlocal

pause



하위폴더의 파일을 상위폴더로 옮기는 배치파일 명령어?를 서치해서 찾았는데요,
이걸 사용하니까 작동은 잘 되는데 파일명을 0001, 0002 이런식으로 변경해서 경로이동을 하더라구요..

기존 파일명 그대로 옮기려면 어떤 부분을 삭제해줘야 할까요?ㅠㅠ 감사합니다.
Tags : 태그가 없습니다.

6개의 답변이 있습니다.

wansoo
  0 추천 | 4년 이상 전

자 그럼...
질문자의 목적에 맞게 비슷한 환경을 만들어 두고 실제 올린 배치 파일을 Test해 보도록 하겠습니다.
첨부 이미지와 같은 디렉터리 구조에 File들을 임의 생성해서 넣고 Test를 진행합니다.
질문자가 올린 배치 file을 test.bat라는 이름으로 d:\ 에  test.bat라는 이름으로 만들고 시작합니다.

test.bat를 실행 시키면 아래와 같은 내용을 표시되고 종료되면서 사용자 입력을 기다리는 화면이 나오네요.

실행되는 화면을 쭉~ 보면 원하지 않았던 “디렉터리가 비어 있지 않습니다.”라는 메시지가 계속 표시되고 있음을 확인할 수 있네요.
디렉터리를 지우기 위해 사용한 rd 명령에서 오류가 일부 발생하고 있는 거 겠죠.

그럼, 실제 실행된 결과를 한번 볼까요.

“sound1”의 하위 디렉터리인 “도시”, “바다” 등에 있던 flac file들이 “sound1” 디렉터리에 0001.flac ~ 0017.flac로 파일명이 숫자로 변경되어 복사되어 있는 것이 보이네요. “도시” 디렉터리에는 flac file들만 있었기 때문에 rd 명령에 의해 정상적으로 지워지고 없지만, flac가 아닌 file들이 있는 디렉터리는 다른 파일들이 남아 있어서 빈 디렉터리가 아니어서 지울수 없다는 오류 메시지가 표시되었던 것 같고요.
남아 있는 파일 정리 정도는 사람이 직접해 주는 게 안전할 것 같고, flac 파일들만 있을 경우에는 문제가 되지 않을 것 같고 해서 rd 명령 수행시 오류 메시지 정도는 그냥 넘어가도 될 것으로 보이네요.

여기까지가 질문자께서 수행한 결과이며, 질문자는 이 배치 file이 어느정도 원하는 결과를 주고 있으나 file명이 숫자로 변경되어 원래 file명을 유지할 수 있게 수정하고 싶다는 내용이 되겠네요.

간단하게 원하는 결과만 얻고 싶다면 move 명령에서 두 번째 파라미터만 살짝 수정해 주면 간단하게 해결될 수 있겠죠.

즉,
call move "%%g" "%z_path%\%%~nxf\%%z_numm%%.%z_ext%" 를,
call move "%%g" "%z_path%\%%~nxf\“
수정하면 원하는 결과를 얻을 수 있게 되는 것이죠.
그럼, 이렇게 해서 배치 파일을 한번 실행해 보도록 하겠습니다.

화면에 표시되는 메시지는 수정전이나 수정 후나 거의 유사하게 표시될 것 같고요.
실제, 결과를 확인해 볼까요.

질문자가 요구한 결과가 산출된 것 같네요.
그런데... 자세히 확인해 보면 한가지 심각한 문제점이 있네요. 동일 이름 ( ex: “숲속.flac” )이 있을 경우에 덮어씌워 버리고 있네요.
자동으로 이름을 변경해서 복사하도록 만드려면 복잡해 질 것 같고... 간단하게 할수 있는 안전 조치로  move 문에 /-y 옵션을 추가하여 사용자에게 어떻게할지 묻도록 만드는게 좋지 않을까 생각되네요.

끝으로, 불필요한 명령들을 주석 처리문인 rem으로 표시하고, 인덴테이션 처리도해서  깔끔하게 정리한 최종 배치 file을 올리면서 마무리 짓겠습니다.

:::::: 불필요한 명령들을 주석 처리 (rem 사용)
@echo off

setlocal
rem cd /d "%~dp0"

set "z_path=D:\test2\테스트1"
set "z_ext=flac"
set "z_file=*"

:: 폴더 리스트

for /f "usebackq delims=" %%f in (`dir /b /ad "%z_path%"`) do (
  rem echo "%z_path%\%%~nxf"
  rem call set /a z_num=0

  for /f "usebackq delims=" %%g in (`dir /b /s "%z_path%\%%~nxf\%z_file%.%z_ext%"`) do (
    rem call set /a z_num=%%z_num%%+1
    rem call set z_numm=00000%%z_num%%
    rem call set z_numm=%%z_numm:~-4%%

    call move /-y "%%g" "%z_path%\%%~nxf\"

    rem ping localhost -n 1 >nul

    rd /q "%%~dpg" >nul
  )
)

endlocal
rem pause

wansoo
  0 추천 | 4년 이상 전

질문자께서 올린 배치 파일 의 명령들을 세부적으로 분석 설명해 드려 보겠습니다.

다른 분들께도 배치 File을 이해하는데 도움이 될 수 있을 거라 생각되네요.

보다 정확한 답글을 달아두어야 이상한 댓글로 인한 오해가 있을 가능성도 낮아 질거 같고요.

그럼, 첫줄 부터 시작하겠습니다.

echo off : 배치 파일 명령 수행중에 명령들이 화면에 표시되지 않게 사용하는 명령으로

이 명령이 없을 경우에는 실행하기 위한 명령과 명령에 의한 결과 값들이

함께 표시되게 되어 화면이 지저분하게 보이게 되어 실 업무에 배치 파일을 사용하게 될 경우에

대부분 제일 첫줄에 echo off 명령을 넣어 사용하게 됩니다.
@echo off 와 같이 제일 앞에 @를 붙이는 이유는 echo off 명령에 의해

이후 부터의 명령은 화면에 보이지 않게 되나, echo off라는 명령 자체는 화면에 표시되게 되기 때문에

echo off라는 명령 조차도 화면에 표시되지 않게 하기 위해 echo off 앞에 @를 붙이게 됩니다.

setlocal : 배치 파일이 실행되면서 변경되는 환경 변수,

디렉터리 경로 등이 배치 수행후 모두 원래 상태대로 남아 있게 하기 위한 명령
          setlocal 다음 부터 endlocal까지 변경되는 환경값들이 endlocal로 인해

setlocal 이전 상태로 되돌아 가게 됨

cd /d "%~dp0" : 배치파일일 실행될때 배치파일의 File 명에 해당하는 기본 환경 변수값이 %0에 할당됨.
                %1은 배치 파일을 수행할때 입력한 첫번째 파라미터값, %2는 두번째, ... 등
                그리고, 환경 변수에서 특정 문자열을 추출할때 ~를 사용하게 되는데, 
                %~d0와 같이 사용할 경우에 %0에 해당하는 파일이 있는 드라이브 문자를 알려 주게 되고,
                %~p0와 같이 사용할 경우에 %0에 해당하는 파일이 있는 경로를 알려 주게 되고요.
                %~dp0와 같이 사용할 경우에는 %0 파일이 있는 드라이브명과 디렉터리 경로를 알려줌.
                디렉터리를 이동하는 명령인 cd 명령으로 "%~dp0"로 이동하게 되면

배치 파일이 있는 디렉터리로 작업 디렉터리를 이동하게 되겠죠.
                /d 옵션은 경로만 옮기지 말고 드라이브까지 옮겨가라는 의미가 되죠.

예를 들어 C 드라이브에서 cd d:\test2라고 명령을 줬을 경우
                작업 드라이브는 C에 그대로 남아 있는 상태에서 작업 경로만 d:\test2로 옮기게 되어

다시 드라이브 이동 명령을 줘야하지만  /d 옵션을 주게 됨으로 인해

작업 드라이브와 경로까지 한번에 옮겨갈 수 있게 됩니다.
                이동해갈 대상 경로 앞뒤에 이중 따옴표를 사용한 이유는 경로명에 빈칸이 있을 경우에

빈칸을 포함해서 한개 경로임을 묶어 주기 위한 것이죠.
                빈칸이 없는 상태의 경로라면 이중따옴표를 사용하지 않아도 되죠. 
                그럴 경우에는 Program Files 와 같은 명칭 중간에 빈칸이 있을 경우에 문제가 생기게 되겠죠.
                이 배치 파일의 목적에 있어서는 이 명령은 굳이 사용하지 않아도 무방할거라 보여 지네요.

set "z_path=D:\test2\테스트1" : 작업 대상 파일들이 있는 경로를 지정하는 환경 변수 설정

set "z_ext=flac" : 작업 대상 파일들의 확장자를 지정하는 환경 변수 설정,

FLAC는 Free Lossless Audio Codec에 해당하는 무손실 오디오 파일에 해당하죠.
                  MP3가 압축을 위해 사람 귀에 영향이 적은 정보들을 제거하는 형식의 손실 압축을 하여

압축 용량을 획기적으로 줄이게 되는데,  사람 귀로는 압축 전후의 음질 변화를

잘 못느낄수 있으나 실제로는 엄청난 데이터 손실이 생기게 되는 압축 방식이죠.
                  즉, MP3로 한번 압축했다면 MP3 파일로는 압축 이전의 오리지날 음질 사운드 파일을

복구해 낼 수 없게 되는데,  FLAC는 오리지날 음질을 그대로 유지하면서도

압축 효율이 상당히 높은 사운드 전용 압축 포멧이죠.
                  MP3에 해당하는 이미지 포멧이 JPG라 할 수 있고,

FLAC에 해당하는 이미지 포멧이 PNG라 할 수 있겠고요.

set "z_file=*" : 작업 대상 파일들의 파일명에 해당하는 환경 변수를 지정하는 것으로

와일드 카드인 *를 사용하여 모든 파일명을 작업 대상으로 지정하고 있죠.


:: 폴더 리스트 : 주석에 해당 하는 내용이고요. 다른 명령을 적기전에 :로 시작하면

주석으로 처리하게 되고요. rem 명령도 주석 처리지만, :를 사용하는게 장점이 많아요.

for /f "usebackq delims=" %%f in (`dir /b /ad "%z_path%"`) do (  : for 반복문에 해당하고,

/f 옵션은 in 문에서 읽은 파일 내용을 대상으로 반복 작업한다는 의미이고,
                usebackq는 back quote를 사용한다는 의미이고, back quote "`"는 키보드에

좌측 상단 ~와 함께 있는 문자로 명령어를 실행용도로 사용되죠.
                즉 /f 옵션에 의해 file을 찾아야 하는데, back quote를 사용하여 명령의 실행 결과를

File 내용처럼 사용한다는 의미
                delims에서 지정한 문자로 문자열을 분리 시키게 되나 "delims=" 처럼 사용할 경우

한줄 전체를 구분자 분리없이 통채로 이용하겠다는 의미

                %%f는  for /f in ( ) 문에 의한 첫번째 열의 값을 저장할 변수를 지정한 것이며
                `dir /b /ad "%z_path%"` 는  z_path에 지정한 작업대상 디렉터리를 dir 명령으로 list하는 명령,

/b 옵션에 의해 경로 명만 표시하게 되고, /ad 옵션에 의해 파일들은 제외하고

디렉터리들만 표시하게 됨


echo "%z_path%\%%~nxf" : 대상 file명들을 표시하는 명령으로 실제 확인하는 용도이며,

실제 사용시에는 불필요한 내용
                        %%~nxf의 의미는 %%f가 for 문에서 사용한 반복에서 하나 하나 가져온 값들을

담는 변수가 되며 문자열에서 특정 내용을 추출하는 용도로 사용되는 ~를 붙이고

file명에 해당하는 n과 확장자를 추출하는 용도인 x 사용.
                        여기서 하나 하나 추출되는 내용이 file이 아닌 디렉터리명이기 때문에

x는 불필요할 것으로 보임
                        즉, 대상 경로에서 하위 디렉터리를 만드는 문구로 x없이

%z_path%\%%~nf"를 사용해도 될 것으로 보임

call set /a z_num=0    : 반복 횟수를 카운트 하기 위한 용도로 사용됨,

batch file에서 환경 변수가 상수로 처리되는 특성이 있기 때문에

그에 따른 문제를 해결하기 위해
                        call 문을 사용해서 환경 변수를 초기화 시키는 형태를 취한 것으로 보임
                        질문자의 요구 작업에는 불필요한 내용( 각 서브 디렉터리별로 카운트를 0부터 다시하게 됨 )


for /f "usebackq delims=" %%g in (`dir /b /s "%z_path%\%%~nxf\%z_file%.%z_ext%"`) do ( : 앞에 for문에

의해 나온 각각의 서브 디렉터리에 대해 다시 반복하기 위한 for문
                        앞의 for문에서 각각의 작업 대상 값을 %%f 변수에 담았고,

여기 for문에서는 %%g 변수에 각각의 대상 값을 저장하게 됨
                          in문 안에 dir 명령에서 /s 옵션을 주어 해당 경로 하위의

모든 파일을 하나하나 추출하는 용도로 사용되고 있음
                          여기서도 dir /b /s "%z_path%\%%~nf\%z_file%.%z_ext%"와 같은 형태로

x를 제거해도 무방할 것으로 보임
                          dir /b /s "D:\test2\테스트1\디렉터리명\*.flac" 명령이 수행되어

해당  디렉터리 하위의 모든 flac 확장자를 가진 file들의 list를 추출해냄


call set /a z_num=%%z_num%%+1  : 순번을 1씩 증가 시키는 명령,

for loop 내에 call 없이 %z_num%을 사용할 경우에 
                                  z_num 값이 loop 들어오기 전의 값으로 상수처리되는

문제가 있기 때문에 call문을 사용했고 
                                  call문을 사용했기 때문에 변수의 앞뒤에 %를 한개씩 더 붙임
                                  질문자의 요구 작업에는 불필요한 내용

call set z_numm=00000%%z_num%%  : 계산되는 값의 앞자리에 0을 5개 더 붙여서

문자열을 만듦
                                  질문자의 요구 작업에는 불필요한 내용

call set z_numm=%%z_numm:~-4%%  : 만들어진 문자열의 뒤에서 4자리만을 추출해냄,

대상 파일명을 0001~9999까지 4자리 file명으로 만들려는 목적
                                  질문자의 요구 작업에는 불필요한 내용

call move "%%g" "%z_path%\%%~nxf\%%z_numm%%.%z_ext%" : file을 옮기는 명령
                                : 2번째 for문에 의해 찾아진 flac file들을

첫번째 for문에 의해 찾아진 서브 디렉터리 안에 0001.flac로 이동시키게됨

ping localhost -n 1 >nul  : 예전 DOS 버전에는 sleep과 같은 명령이 없어

ping 명령으로 1초 정도 대기하게 하고 있음, 
                            없어도 무방할 것으로 보임, 불필요하게 작업시간을 지연시키게 하는 요소가 됨
                            >nul 은 화면에 출력되는 내용들을 화면에 표시되지 않게 리다이렉트 시키는 용도

rd /q "%%~dpg" >nul :  rd 명령은 file 및 디렉터리를 지우는 용도이며

2번째 for문에 의해 추출되어 저장되는 %%g에서 (d) 드라이브 명과 (p) 경로를 추출해냄
                      두번째 for문에 의해 flac file들만 추출해 내어 옮기고 나머지 남아

다른 다른 file들과 디렉터리를 제거하는 용도
)    : 두번째 for 반복문의 끝

)    : 첫번째 for 반복문의 끝

endlocal  : setlocal의 끝을 표시

pause    : 작업 마무리 후 사용자 확인을 위해 잠시 대기



오늘은 여기까지 하고 다음에 다시 질문자께서 요구하는 정리된  batch  file과 실행 결과에 대한 캡쳐 화면등을 올릴 수 있도록 해 보겠습니다.

긴 시간을 내어 이렇게 다시 답변을 다는 이유는 아래 게시글과 관련이 있고요.

https://www.sharedit.co.kr/freeboards/21313

빨간신발 | 4년 이상 전

이렇게 상세하게 답글을 남기시고 역시 명불허전이네요
guest
  0 추천 | 4년 이상 전

ㅇ_ㅇ 붙여넣기도 잘안되는 사이트넹...ㅋㅋㅋ

답변 달기도 힘들다..

@echo off
setlocal
cd /d %~dp0
set "z_source=.\hhhh"
cd /d "%z_source%"
for /f "usebackq delims=" %%f in (`dir /b /s /ad`) do (
echo deep-1 move "%%f\*.*" "../%%f"
echo deep-2 move "%%f\*.*" "%z_source%"
)
endlocal
pause



guest | 4년 이상 전

아래 님들 전부 장풍쏘고 구음진경 독거구검 막 날리시는데...''' 무섭다...- _-'''' deep-1 은 1계층 폴더 depp-2 는 2계층 이상의 폴더... 장풍 쏘고 검풍 날리고 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

wansoo | 4년 이상 전

이분은 기본이 좀 안되어 있는 것 같네요. 새벽에 잠이 덜깬 상태로 글을 적어서 그런지는 잘 모르겠지만... 반말로 글을 적는것도 기본 예의가 없고... 부산에서 서울 가는 길이 경부 고속도로 하나만 있지 않아요. 새로 뚫린 고속도로도 있고, KTX 기차도 있고, 항공편도 있고, 국도로 삥삥 둘러 가는 길도 있고, 배타고 가는 길도 있고 수없이 많이 길이 있어요. 질문 내용이 무엇인지 부터 보세요. 배치 파일 명령어를 질문하신분이 기본으로 제시해 두었어요. 그 배치 파일 내용 중에서 어떤 내용을 변경하면 되는지를 질문한 거에요.
danis78
  0 추천 | 5년 이하 전 | 제이컴즈 | 010-2871-8756
아래 내용만 삭제해도 되지 않나요??
제가 직접 테스트는 안해보고 그냥 배치문 보고 바로 코멘트 적은 것 입니다. 

call set /a z_num=%%z_num%%+1
call set z_numm=00000%%z_num%%
call set z_numm=%%z_numm:~-4%%

wansoo | 5년 이하 전

파일명에 차례대로 번호를 주기 위해 사용하고 있는 환경 변수와 연산식이죠~ move 명령에 해당 환경 변수를 사용하지 않을 경우에는 제거해 주는게 깔끔할것 같고요. move 명을 수정하지 않고 이 명령들만 제거해 준다면... target 경로에 file명은 Null 처리되고, .확장자의 형태로 파일이 복사되어 버려... 모든 file들이 파일명이 없는 한개 file로 복사되게 되는 문제가 생기게 될 것으로 보이네요.

wansoo | 5년 이하 전

저 위에 있는 set "z_ext=flac" 명령까지 제거해 줘 버린다면 확장자까지 null이 되어 버려 move 문을 수정하지 않아도 target 폴더에 .으로 복사하라는 의미가 되게 되는데... 유닉스/리눅스/DOS에서 . 이 File이 아닌 해당 디렉토리를 의미하는 용도로 사용되기 때문에 move 문을 수정하지 않고도 원하는 결과를 얻을 수 있을 것으로 보이네요.

wansoo | 5년 이하 전

그런데... set "z_ext=flac" 명령을 제거했을 경우에 생기는 문제는 for /f "usebackq delims=" %%g in (`dir /b /s "%z_path%\%%~nxf\%z_file%.%z_ext%"`) 명령에서도 확장자가 NULL 처리되어 버려 복사 대상이 되는 file들이 확장자가 없는 file들만 선택되어 버려 잘못된 결과가 얻게 되어 버리겠네요~

wansoo | 5년 이하 전

결론은 move 문을 수정해 주고... 말씀하신 3개 라인들을 제거해주는게 원하는 결과를 얻을 수 있고, 코드도 깔끔하게 된다 이겠네요~ ^^
Genghis Khan
  0 추천 | 5년 이하 전
@ECHO OFF >NUL
SETLOCAL enableextensions
set "folderIn=D:\test2\테스트1"

move "%folderIn%\*.*" "%~1"

ENDLOCAL
goto :eof

D: 드라이브에 \Test2\테스트1\ 파일을 만들어  Test해보니 Test2로 무브 됩니다.
기존 파일 삭제할 필요도 없구요
간단하게 이렇게 하면 되나요?
좀더 디테일하게 하려면 이렇게 ?
 @ECHO OFF >NUL SETLOCAL enableextensions set "folderIn=D:\test\Files In" set "folderOu=D:\test\Files Out" set "logFile=%folderOu%\movelog.txt" set "logToDo=>> "%logFile%" 2>&1" rem above line causes that moved file names will be logged to file %logFile% rem unrem next line if you need no log  rem set "logToDo=" set /A "filesInNo=0" for /f "tokens=*" %%# in ('dir /B "%folderIn%\*.*" 2^>NUL') do set /A "filesInNo+=1" (   echo === %date% %time% === %filesInNo%   if %filesInNo% GTR 0 (       move /Y "%folderIn%\*.*" "%folderOu%\"   ) else (       rem Nothing to copy   ) )%logToDo% rem next line for debugging purposes only if not "%*"=="" type "%logFile%" ENDLOCAL goto :eof   

wansoo | 5년 이하 전

@echo off 에 > NUL을 사용할 필요는 없어요~ 왜냐면... 제일 앞에 @가 >NUL 역할을 하고 있기 때문이죠~ ^^

wansoo | 5년 이하 전

move "%folderIn%\*.*" "%~1" 는 대충 봐도 문제가 많이 있는 걸로 보이네요~ 우선, folderIN 환경 변수에 지정한 폴더 안에 있는 File들만 이동 대상이 되겠고... 즉 하위 폴더들과 하위 폴더들에 있는 파일들은 대상에 포함되지 않게 될걸로 보이고요. 그리고... %~1이 어떤 역할을 하게 될까요...??
wansoo
  0 추천 | 5년 이하 전
해당 배치 파일을 직접 실행해 보진 않아서...
작동이 정말 잘 되는지 안되는지는 확인 못해 봤는데요.
질문하신분께서 작동이 잘되는데, 파일 명만 원치 않는 형태로 변경된다 하시니...

File을 복사하는 부분에서 파일 명을 변경 처리 하고 있기 때문에 해당 문제가 발생되겠고요.

파일을 복사하는 부분은  아래 쪽에서 7번째 라인 정도에 있는
call move "%%g" "%z_path%\%%~nxf\%%z_numm%%.%z_ext%"
가 되겠고요.

%%g가 해당 서버 폴더에 있는 파일 각각에 대한 전체 경로일 거 같고요.
%z_path%\%%~nxf\%%z_numm%%.%z_ext% 가 복사될 파일의 파일명 및 확장자 포함한 전체 경로가 되겠고요.
복사 대상 경로에서 File 명에 해당하는 부분만 제거해 버리면 될것으로 보이는데...
%z_path%\%%~nxf\%%z_numm%%.%z_ext% 을
%z_path%\%%~nxf\% 으로 변경하면 될거라 생각되네요.

즉, 
call move "%%g" "%z_path%\%%~nxf\%%z_numm%%.%z_ext%" 부분을
call move "%%g" "%z_path%\%%~nxf\" 으로 수정하면 원하는 결과가 나올거 같아 보이네요.

이렇게 함으로 인해 불필요하게 남아 있는 코드들은 계속 남아 있긴 하겠고...
서브 폴더들에 있는 파일명이 같은 파일 들이 상위 폴더에 복사되면서 중첩되면서 파일을 오버라이트 할지 어떻게 할지에 대한 경고 메시지가 나오게 될 수도 있을 것 같고요.

깔끔하게 만들려면 시간을 가지고 다시 봐야 겠지만...
대충 봤을 경우에는 

아래에서 7번째 줄 라인을 
call move "%%g" "%z_path%\%%~nxf\"
으로 수정하면 되지 않을까 싶네요~