DataBase/Oracle2011. 6. 2. 11:47

해당 테이블의 컬럼 속성이 무엇인지 확인 후 해당 타입일 때 특정 조건 작업할때 유용



 테이블 조회
select * from user_objects where object_type='TABLE'


 컬럼 조회
select * from cols where table_name='테이블 이름'

Posted by penguindori
Common JAVA2011. 5. 23. 21:43


<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" import="java.io.*"%>
<%
 OutputStream outStream = null;
 FileInputStream fileStream = null;
 try {

  request.setCharacterEncoding("euc-kr");
  String fileName = request.getParameter("file");

  //또는 아래 사용
  //String filePath = request.getRealPath("/")+"uploadFile/";
  //request.getParameter("filepath");
  //추후 파라미터로 데이터를 받을 예정
  String filePath = "D:\\FileManage\\uploads\\";  

   response.setContentType("application/x-msdownload");
   //위 세팅으로 안될 경우에 사용.
   //response.setContentType("application/octet-stream");

   String convName1 =
   java.net.URLEncoder.encode(fileName,"UTF-8");

   response.setHeader("Content-Disposition", "attachment;filename=" + convName1 + ";");

  //위 세팅으로 안될 경우에 사용.
  //response.setHeader("Content-Disposition","attachment;fileName=\""+Convfilename+"\";");

   String convName2 = fileName;
   File file = new File(filePath+convName2);
 
   byte[] byteStream = new byte[(int)file.length()];
   fileStream = new FileInputStream(file);
   int i=0;
   int j=0;
   while( (i=fileStream.read()) != -1 ){
    byteStream[j] = (byte)i;
    j++;
   }
  out.clear(); //out--> jsp자체 객체
  out=pageContext.pushBody(); //out--> jsp자체 객체
  outStream = response.getOutputStream();
  outStream.write(byteStream);


 }catch(Exception e) {
  System.out.println(e);
 }finally {
  if(fileStream != null) fileStream.close();
  if(outStream != null) outStream.close();
 }

 


%>

Posted by penguindori
카테고리 없음2011. 5. 16. 19:58

*
* 로그인 중복체크
* 이미 로그인한 사용자가 있을경우 기존의 사용자 세션을 종료후 자신이 로그인.
* 해시테이블에 세션과 접속자 아이디를 저장해 둔다.
* 세션 Object를 저장하는 이유는 동일한 아이디로 재접속 했을경우
* 아이디로 세션Object를 찾아내어 기존의 접속을 끊기위해서다.(invalidate)
*/


============== WEB-INF\src\test\LoginManager.java ===============
package test;

import java.util.*;
import javax.servlet.http.*;
/*
* session이 끊어졌을때를 처리하기 위해 사용
* static메소드에서는 static만사용 하므로static으로 선언한다.
*/
public class LoginManager implements HttpSessionBindingListener{

    private static LoginManager loginManager = null;
    
    //로그인한 접속자를 담기위한 해시테이블
    private static Hashtable loginUsers = new Hashtable();
    
    /*
     * 싱글톤 패턴 사용
     */
    public static synchronized LoginManager getInstance(){
        if(loginManager == null){
            loginManager = new LoginManager();
        }
        return loginManager;
    }
    
    
    /*
     * 이 메소드는 세션이 연결되을때 호출된다.(session.setAttribute("login", this))
     * Hashtable에 세션과 접속자 아이디를 저장한다.
     */
    public void valueBound(HttpSessionBindingEvent event) {
        //session값을 put한다.
        loginUsers.put(event.getSession(), event.getName());
        System.out.println(event.getName() + "님이 로그인 하셨습니다.");
        System.out.println("현재 접속자 수 : " +  getUserCount());
     }
    
    
     /*
      * 이 메소드는 세션이 끊겼을때 호출된다.(invalidate)
      * Hashtable에 저장된 로그인한 정보를 제거해 준다.
      */
     public void valueUnbound(HttpSessionBindingEvent event) {
         //session값을 찾아서 없애준다.
         loginUsers.remove(event.getSession());
         System.out.println("  " + event.getName() + "님이 로그아웃 하셨습니다.");
         System.out.println("현재 접속자 수 : " +  getUserCount());
     }
    
    
     /*
      * 입력받은 아이디를 해시테이블에서 삭제.
      * @param userID 사용자 아이디
      * @return void
      */
     public void removeSession(String userId){
          Enumeration e = loginUsers.keys();
          HttpSession session = null;
          while(e.hasMoreElements()){
               session = (HttpSession)e.nextElement();
               if(loginUsers.get(session).equals(userId)){
                   //세션이 invalidate될때 HttpSessionBindingListener를
                   //구현하는 클레스의 valueUnbound()함수가 호출된다.
                   session.invalidate();
               }
          }
     }
    
    
     /*
      * 사용자가 입력한 ID, PW가 맞는지 확인하는 메소드
      * @param userID 사용자 아이디
      * @param userPW 사용자 패스워드
      * @return boolean ID/PW가 일치하는 지 여부
      */
     public boolean isValid(String userId, String userPw){
        
         /*
          * 이부분에 Database 로직이 들어간다.
          */
         return true;
     }


    /*
     * 해당 아이디의 동시 사용을 막기위해서
     * 이미 사용중인 아이디인지를 확인한다.
     * @param userID 사용자 아이디
     * @return boolean 이미 사용 중인 경우 true, 사용중이 아니면 false
     */
    public boolean isUsing(String userID){
        return loginUsers.containsValue(userID);
    }
    
    
    /*
     * 로그인을 완료한 사용자의 아이디를 세션에 저장하는 메소드
     * @param session 세션 객체
     * @param userID 사용자 아이디
     */
    public void setSession(HttpSession session, String userId){
        //이순간에 Session Binding이벤트가 일어나는 시점
        //name값으로 userId, value값으로 자기자신(HttpSessionBindingListener를 구현하는 Object)
        session.setAttribute(userId, this);//login에 자기자신을 집어넣는다.
    }
    
    
    /*
      * 입력받은 세션Object로 아이디를 리턴한다.
      * @param session : 접속한 사용자의 session Object
      * @return String : 접속자 아이디
     */
    public String getUserID(HttpSession session){
        return (String)loginUsers.get(session);
    }
    
    
    /*
     * 현재 접속한 총 사용자 수
     * @return int  현재 접속자 수
     */
    public int getUserCount(){
        return loginUsers.size();
    }
    
    
    /*
     * 현재 접속중인 모든 사용자 아이디를 출력
     * @return void
     */
    public void printloginUsers(){
        Enumeration e = loginUsers.keys();
        HttpSession session = null;
        System.out.println("===========================================");
        int i = 0;
        while(e.hasMoreElements()){
            session = (HttpSession)e.nextElement();
            System.out.println((++i) + ". 접속자 : " +  loginUsers.get(session));
        }
        System.out.println("===========================================");
     }
    
    /*
     * 현재 접속중인 모든 사용자리스트를 리턴
     * @return list
     */
    public Collection getUsers(){
        Collection collection = loginUsers.values();
        return collection;
    }
}

/*
* 거의 대부분의 웹사이트를 보면 브라우저를 열고 로그인후
* 또다시 다른브라우저를 열고 로그인을 하면 로그인이 되는것을 확인하실수
* 있습니다. 이는 즉... 여러곳에서 동일한 아이디로 접속을 할수있다는 예입
* 니다. 이와 반대로 메신저같은경우는 이미 로그인이 되어있을시 다른곳에서
* 로그인을 하면 접속을 끊을지를 물어보는 기능도 보셨을 겁니다. 이를 웹에
* 서 구현하여 보았습니다.
* 본 예제소스는 우리가 구현하려고 예제에서 가장 핵심적인 부분을 맡고있
* 는 소스입니다.
* 여기서 HttpSessionBindingListener는 서블릿 컨테이너에서 세션이 끊길때
* (valueUnBound)와 이를 구현하는 오브젝트가 해당 세션에 setAttribute될
* 때(valueBound) 호출합니다. 굳이 이를 구현하는이유는 세션이 끊기는 시
* 점을 정확히 잡아내기 위함입니다. 사용자가 로그아웃버튼을 누를시도 있지
* 만 세션이 타임아웃되는경우도 세션이 끊겨야 하기 때문입니다. 그리고
* 브라우저의 닫기버튼, Alt+F4, Ctrl+E버튼 을 누를시 이벤트를 잡는방법도
* 차근차근 알아보도록 합시다.

=============================== login.jsp ============================
<%
    /*
     * 로그인 페이지, 로그인전 현재 로그인된 이용자수를 출력한다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"%>
<%@ page import="test.LoginManager"%>
<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance();
%>
<%
    //login_try에서 로그인을 하지 않을경우 세션에 남아있는 userId를 제거한다.
    session.removeAttribute("userId");
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body>
    <h3 align="center">현재 접속자 수 : <%=loginManager.getUserCount() %>명</h3>
    <form action="login_try.jsp" name="login">
        <div align="center">
            아이디  :   <input type="text" name="userId"><br>
            비밀번호    :   <input type="passward" name="userPw"><br>
            <input type="submit" value="로그인">
        </div>
    </form>
</body>
</html>




============================= login_try.jsp ============================
<%
    /*
     * 로그인 시도페이지, id, pw유무를 체크하고, 올바르다면
     * 이미 접속한 아이디인지 체크한다. 이미 접속한 아이디라면
     * 기존 접속을 유지할것인지, 기존접속을 kill시키고 로그인할것인지를
     * 확인한다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%@ page import="test.LoginManager"%>
<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance();
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body align="center" valign="center">
<%
    String userId = request.getParameter("userId");
    String userPw = request.getParameter("userPw");
    
    //아이디 패스워드 체크
    if(loginManager.isValid(userId, userPw)){
        
        //접속자 아이디를 세션에 담는다.
        session.setAttribute("userId", userId);
        
        //이미 접속한 아이디인지 체크한다.
        //out.println(userId);
        //out.println(loginManager.isUsing(userId));
        loginManager.printloginUsers();
        if(loginManager.isUsing(userId)){
%>
            이미 접속중입니다. 기존의 접속을 종료하시겠습니까?<P>
            <a href="disconnect.jsp">예 </a>
            <a href="login.jsp">아니오</a>
<%
        }else{
            loginManager.setSession(session, userId);
            response.sendRedirect("login_ok.jsp");
        }
%>
<%
    }else{
%>
        <script>
            alert("로그인후 이용해 주세요.");
            location.href = "login.jsp";
        </script>
<%
    }
%>
</body>
</html>




========================== disconnect.jsp ============================
<%
    /*
     * login_try.jsp에서 로그인 중복시 무시하고 로그인할경우 호출.
     * 기존의 session을 끊고 hashTable에 저장후 login_ok.jsp를 호출.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%@ page import="test.LoginManager"%>
<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance();
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body>
<%
    String userId = (String)session.getAttribute("userId");
    if(userId != null){
        //기존의 접속(세션)을 끊는다.
        loginManager.removeSession(userId);
        
        //새로운 세션을 등록한다. setSession함수를 수행하면 valueBound()함수가 호출된다.
        loginManager.setSession(session, userId);
        response.sendRedirect("login_ok.jsp");
    }
%>
</body>
</html>




============================= login_ok.jsp ============================
<%
    /*
     * 정상적으로 로그인되었을경우 호출
     * 접속자 아이디를 보여주고 현재 접속중인 모든 사용자를 뿌려준다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%@ page import="java.util.*, test.LoginManager"%>

<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance();
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body align="center" valign="center">
<%
    //jsp내장객체 session을 이용하여 접속자 아이디를 얻어온다.
    String userId = (String)session.getAttribute("userId");

    if(userId != null){
%>
        <%=userId%>님 환영합니다.
        <a href="logout.jsp">로그아웃</a>
        <p>
        현재 접속자 : <br>
<%
        Collection collection = loginManager.getUsers();
        Iterator iterator = collection.iterator();
        int i=0;
        while(iterator.hasNext()){
            out.print((++i)+". "+iterator.next()+"<br>");
        }
    }else{
%>
        <script>
            alert("로그인후 이용해 주세요.");
            location.href = "login.jsp";
        </script>
<%
    }
%>
</body>
</html>



============================= logout.jsp ==============================
<%
    /*
     * 로그아웃을 클릭했을때 호출된다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%
//session을 확~~~끊어 버린다. 이시점에 LoginManager의 valueUnbound()가 호출된다.
session.invalidate();
response.sendRedirect("login.jsp");
%>

지금까지 중복로그인 체크를 막기위한 테스트를 해 보았다.

처음 강좌에서 얘기했던 윈도우 닫기버튼,

윈도우 닫기 단축키인 Alt+F4, Ctrl+E를 수행했을때 세션을 끊는방법을

알아보겠다. 우리가 아래 예제를 수행했던 이유를 먼저 알아보자.

특정 사용자가 로그인을 시도한다고 생각해보자.

일반 웹사이트에서는 로그인을하고 다른쪽에서

로그인을 시도할경우 아무런 대책없이 로그인을 허용하였다.

우리가 많이 사용하는 메신저의 예를 들어보자.

메신저의 경우 로그인을 했을시 이미 접속중이라는 메시지가 뜨게된다.

만약 위와같은 메시지가 수시로 뜬다면

자신의 id가 누군가가 도용해서 사용중이라는 사실을 알수있을것이다...

그런 느낌이 온다면 우리는 id, password를 변경하여 정보누출을

어느정도 막을수 있을것이다.

그리고 또하나 자신의 계정으로 접속중에 제 3자의 누군가가 로그인을 했다면

누군가가 다른곳에서 접속중이라는 메시지를 뿌려줄수 있다면 훨씬 더 좋을

것이다.

우리가 만든 예제에서는 몇가지 문제점이 있다.

위기능을 사용하기 위해서는 로그인시 누군가가 자신의 id로

이미 로그인 중인지를 알아내는것이 중요하다.

그리고 이미 로그인된것이 확인 되었다면 자신이 로그인하기를 원한다면

이미 로그인한 세션을 끊고 자신의 세션을 등록해야한다..

그러기 위해서 우리는 해시테이블을 사용하였으며..

기존의 세션을 끊기위해서 세션 오브젝트를 직접 담았다.

문제점은 누군가가 자신의 id로 이미 로그인중인지 알아내는것이다.

우리가 로그인을 하고 무조건 로그아웃버튼을 클릭했다면 그당시 세션을 끊어

다른곳에서의 로그인을 바로 허용하면 되지만 닫기버튼을 누를때나

Alt+F4, Ctrl+E를 누를때 이벤트를 잡지못하면 안된다는것이다.

그렇지 못하면 그 세션은 서버의 메모리에 계속 살아서 세션이 타임아웃되기

까지는 계속 살아있을것이다.(이때는 HttpSessionBindingListener의 valueUnbound함수가 서블릿 컨테이너에서 호출한다.)

정상적으로 로그아웃버튼을 누르고 브라우저를 닫는사람이 어디있겠는가?

대게의 사용자들은 로그아웃을 하지 않고 바로 닫아버릴것이다.

여기서 위의 방법으로 닫았을때 이벤트를 잡을수 있는 방법을 설명하겠다.

예제는 간단하다.




============================== main.jsp ==============================
<frameset rows="0,*" border="0">
    <frame name="duplChkFrame" scrolling=no frameborder=0 marginwidth="0" marginheight="0" src='frame.html' noresize>
    <frame name="topFrame" scrolling="no" frameborder=0 marginwidth="0" marginheight="0" target="mainFrame" src="mainFrame.jsp" noresize>
</frameset>


============================== frame.html' ============================
<html>
<head>
<script>
    /**
    * logout()
    * 작 성 자 : 권홍재
    * 작 성 일 : 2006-12-18
    * 개    요  : 브라우저가 닫길시 호출
    * return값 : void
    */
    function logout(){
        location.href = 'logout.jsp';
    }
</script>

</head>
<body OnUnload="logout()">
</body>
</html>






위처럼 onUnLoad이벤트를 사용하면 된다.

프레임으로 나눈 이유는 브라우저가 하나의 프레임만으로 되어있다면..

페이지 이동시마다 onUnLoad이벤트가 발생한다는것이다..

곧 페이지가 이동시마다 로그아웃이 발생한다는뜻이며

고로 세션이 끊겨서 페이지를 이동할수 없다는 말이 된다.

이를 방지하기 위해서 프레임으로 나누어서 메인 프레임에는

일반적인 페이지를 호출을 하고 로그아웃 페이지를 호출하는부분은

페이지가 변경되지 않는 frame.html에서 로그아웃 처리를 하는것이다.

페이지가 닫길때 frame.html에서 onUnLoad이벤트가 발생하여

logout.jsp를 호출하는것이다.

참... 그리고 한가지 새로고침버튼이나 F5번버튼 마우스 오른쪽 버튼을

클릭하고 새로고침을 할경우는 어쩔수 없다는 것이다..

페이지를 다시 부르는것이기때문에 페이지를 부르기전 기존페이지가

죽기전에 onUnload이벤트가 발생한다는 것이다. 이를 해결하기 위해서는

마우스 오른쪽버튼 상단의 메누 보이지 않기, 키업이벤트에서 F5번키를

막는수밖에는 없다.^^;

이것으로 중복로그인체크 강좌를 마치겠다...^^*

 

Posted by penguindori
카테고리 없음2011. 5. 3. 18:01

InputStream inpputStream = getServletcontext().getResourceAsStream("/WEB-INF/properties/db.properties");

Properties props = new Properties();
props.load(inpputStream);

String diver = props.getProperty("diver").toString();


String url = props.getProperty("pool.url");
String user = props.getProperty("pool.user");
String upwd = props.getProperty("pool.password");

Class.forName("oracle.jdbc.driver.OracleDiver");
conn = DriverManager.getConnection(url, user, pwd);

Posted by penguindori
20112011. 4. 29. 09:06


1. Apache+Tomcat 6.0
  
   path : C:\Program Files\Apache Software Foundation\Tomcat 6.0

   setting : \conf\server.xml
 
  추가된 항목
 <Host name="localhost" appBase="D:\FileManage">
   <Context path="" docBase="D:\FileManage" debug="0">
      <Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_access_log." suffix=".txt" directory="logs" timestamp="true" />
   </Context>
  </Host>

 

2. Jakarta Isapi Redirector

   path : C:\Program Files\Apache Software Foundation\Jakarta Isapi Redirector
 


3. 기본 WEBROOT 경로
 
  
    D:\FileManage


4. 기타 IIS6.0 Setting 작업 (인터넷 정보 서비스(IIS) 관리

   1. 웹서비스 확장
       - jakarta 추가 작업

   2. 웹 사이트
      - fimeManage  추가 작업

Posted by penguindori
DataBase/Oracle2011. 3. 22. 21:07
select SQL_TEXT, FIRST_LOAD_TIME from v$sqlarea
Posted by penguindori
카테고리 없음2011. 3. 22. 13:33
Posted by penguindori
카테고리 없음2011. 3. 22. 11:10

저도 초보 수준인데 이런 말씀 드리긴 뭐하지만, 이 글은 초보 분들에게 별 재미가 없을테니 초보 분들은 그냥 넘겨주세요. 배치파일 자주 작성하시는 분들을 위한 글입니다.

저는 FOR 구문에 대해 특별히 공부한 것이 없습니다. 그냥 /? 붙여서 나온 도움말을 보고 익힌게 다입니다. 그래서 여기서 다룰 내용은 매우 기초적인 것들입니다. 하지만 이러한 기초적인 내용을 익혀두면 응용하기에 따라 매우 편리하게 사용할 수 있습니다. 제가 윈도우 하드에서 설치하기나 PE, VHD 관련 글에 첨부하는 스크립트에는 대부분 FOR 구문이 포함되어 있습니다. 조금만 익혀두면 적어도 제가 사용하는 수준만큼은 도달할 수 있습니다.



1. 기본 구조

for /f "옵션" %변수 in (메롱) do 명령어

FOR 구문의 기본 구조입니다. (메롱) 안에 있는 문자열, 파일, 명령어의 출력값 등에 대해 DO 이하 명령어를 반복 수행하도록 지시합니다. 그런데 %변수의 경우 일괄 배치파일 안에 들어갈 때는 %%변수 형태로 적어줘야 합니다. 또한 %변수는 대소문자를 구분합니다. 즉 %a와 %A는 다릅니다.

예제)
for %a in (*.msu) do start /wait wusa %a /quiet /norestart

위 예는 현재 위치에 존재하는 확장자 msu 파일에 대해 wusa라는  "윈도우 업데이트 독립 실행형 설치 관리자" 툴을 사용하여 반복해서 설치를 수행합니다. 아 물론 이건 윈도우 비스타, 세븐 기준입니다.

XP 업데이트라면 아래 예제처럼 하면 되겠지요.

예제)
for %a in (*.exe) do start /wait %a /quiet /norestart

위 예는 현재 위치에 존재하는 확장자 exe 파일에 대해 반복해서 실행을 수행합니다.


예제)
for %%a in (C: D: E: F: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: W: X: Y: Z:) do (
     if exist %%a\WIN51 set CDROM=%%a
)

위 예는 C ~ Z 드라이브 중에 WIN51 이라는 파일을 포함하고 있다면 해당 드라이브를 CDROM 이라는 변수로 지정해줍니다. 제가 WIN51 이라는 태그파일을 넣은 이유는 XP 설치 CD를 예로 든 것입니다. 만약 XP 무인설치를 구성하는데 추가 프로그램이 CD롬에서 실행되어야 한다면 CD롬 드라이브 문자를 알아내기 위해 저렇게 사용할 수 있겠지요. 이렇게 한번 지정해두면 예를 들어 CD롬 안에 UTILS 폴더가 있고 그 안에 있는 AAA.EXE 파일을 실행해야 한다면

%CDROM%\UTILS\AAA.EXE

위 예처럼 넣으면 되겠지요.



2. 옵션

저는 tokens= 옵션이 FOR 구문의 꽃이라 생각합니다. 토큰을 잘 활용하면 재미있는 스크립트를 많이 만들어낼 수 있습니다. 제가 자주 사용하는 옵션 몇가지만 간단히 설명드리겠습니다.

tokens=
FOR 구문은 각 행별로 분석하는데 이때 토큰은 각 행의 몇번째 문자열을 전달해줄지 지정합니다. 문자열의 기본 구분단위는 공백입니다.

for /f "tokens=3" %a in ("I LOVE YOU") do echo %a

위 예제를 입력하면 결과가 어떻게 될까요? 바로 세번째 문자열인 YOU가 출력됩니다. 토큰이 뭔지 이해되시죠?


delims=
토큰은 기본적으로 공백으로 구분하는데 delims 뒤에 구분 문자를 지정하면 그걸로 대체됩니다.

for /f "tokens=3" %a in ("I LOVE YOU-SO MUCH") do echo %a

for /f "tokens=2 delims=-" %a in ("I LOVE YOU-SO MUCH") do echo %a

for /f "tokens=4 delims=- " %a in ("I LOVE YOU-SO MUCH") do echo %a

위 예제를 각각 입력해보시면 delims의 기능을 이해하실 수 있을겁니다.
첫번째 줄은 delims가 없기 때문에 공백이 구분 문자라서 세번째 토큰은 YOU-SO가 됩니다.
두번째 줄은 - 문자가 구분자라서 두번째 토큰은 SO MUCH가 됩니다.
세번째 줄은 - 와 공백이 동시에 구분자라서 네번째 토큰은 SO가 됩니다.


skip=
위에서부터 몇줄까지 무시할지 결정해줍니다. 필요한 토큰이 세번째 줄에 있다면 처음 두줄은 무시해줘도 되겠지요. 이 경우 skip=2 라고 입력하면 됩니다.


usebackq
이 옵션은 공백을 포함한 파일 경로 양쪽에 큰 따옴표를 붙여줄 때 사용합니다. 대신 이 옵션을 사용하면 괄호안에 들어갈 집합에 대한 표시를 조금 다르게 해야합니다. 원래는

(파일), ("문자열"), ('명령어') 인데 usebackq 옵션을 사용하면 ("파일"), ('문자열'), (`명령어`) 형태로 써야합니다.

예를 들어 MY NAME.TXT 라는 공백을 포함한 이름의 텍스트 파일이 존재하는 경우

for /f "tokens=1" %a in (MY NAME.TXT) do echo %a

for /f "tokens=1" %a in ("MY NAME.TXT") do echo %a

for /f "tokens=1 usebackq" %a in ("MY NAME.TXT") do echo %a

위 3가지 예중에 정상적으로 되는건 세번째밖에 없습니다.
첫번째 줄은 MY 라는 파일과 NAME.TXT 라는 파일을 찾기 때문에 실패하고
두번째 줄은 "MY NAME.TXT"를 파일 이름이 아닌 하나의 문자열로 인식하기 때문에 MY가 출력되고
세번째 줄만 원래 의도대로 MY NAME.TXT 파일 안에서 각 행의 첫번째 토큰을 출력해줍니다.
그러니까 FOR 구문에서 뒤쪽에 띄어쓰기 문제로 큰 따옴표를 사용해야 한다면 usebackq 옵션을 넣고 형식에 맞게 변경해야 합니다. 비단 괄호 안에 들어가는 파일 집합 뿐 아니라 DO 이하 구문에 들어가는 파일 경로에도 동일하게 적용된다는 점 알아두시면 됩니다.


/L
이 옵션은 단계적으로 증가, 감소하는 숫자 집합을 이용할 때 씁니다.

for /l %a in (1,1,5) do start /wait imagex /export h:\sources\install.wim %a e:\install.wim

위 예제는 Imagex로 윈도우 7 통합본 만들 때 일일이 export 시키는게 귀찮은 경우 사용할 수 있습니다. %a를 1부터 5까지 순서대로 받아주기 때문에 h:\sources\install.wim 이미지의 1~5 인덱스를 순서대로 e:\install.wim 파일로 export 시켜줄 수 있습니다. 만약 (5,-1,1) 하시면 5부터 1까지 차례대로 감소하면서 진행됩니다. 그러니까 (시작, 간격, 끝) 형태로 사용하시면 됩니다.



3. 실전 예제

현재 윈도우 7 사용중이신 분들의 에디션을 화면에 출력해보겠습니다. 에디션은 C:\Windows\System32\spp\tokens\skus 폴더 안을 보시면 확인이 가능합니다. 또는 레지스트리 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion 을 통해서도 확인할 수 있습니다. 이 두가지 방법을 각각 FOR 옵션에 적용해보겠습니다.

먼저 토큰 폴더를 활용하려면

dir /b C:\Windows\System32\spp\tokens\skus

이렇게 입력하면 저같은 경우

Security-SPP-Component-SKU-Ultimate

이렇게 출력되는데 이건 공백 기준으로 하나의 토큰이므로 delims=- 를 사용하여 구분해주면 에디션은 다섯번째 토큰이 됩니다.

@echo off
for /f "tokens=5 delims=-" %%a in ('dir /b %windir%\System32\spp\tokens\skus') do set sku=%%a
echo.
echo      당신은 현재 %SKU% 에디션을 사용하고 있습니다.
echo.
pause
exit

위 스크립트를 실행하면 화면에 에디션이 출력되겠지요?

다음은 레지스트리로 해봅시다.

reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v EditionID

이렇게 입력하면


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
    EditionID    REG_SZ    Ultimate

저의 경우 이렇게 출력되는데 첫줄은 공백이고 에디션은 세번째 줄의 세번째 토큰입니다. 따라서 이렇게 구성하면 되겠네요.

@echo off
for /f "tokens=3 skip=2" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v EditionID') do set SKU=%%a
echo.
echo      당신은 지금 %SKU% 에디션을 사용하고 있습니다.
echo.
pause
exit

위 스크립트를 실행해도 마찬가지로 화면에 에디션이 출력됩니다.


위 두가지 예제를 어디에 활용할 수 있을까요? 예를 들어 윈도우 7 $OEM$ 배포폴더 SetupComplete.cmd 파일에 처리해주면 에디션을 자동으로 인식해서 시디키를 입력해줄 수 있겠지요?

@echo off
for /f "tokens=5 delims=-" %%a in ('dir /b %windir%\System32\spp\tokens\skus') do set sku=%%a
if /i %sku%==starter cscript %windir%\system32\slmgr.vbs /ipk xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
if /i %sku%==homebasic cscript %windir%\system32\slmgr.vbs /ipk xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
if /i %sku%==homepremium cscript %windir%\system32\slmgr.vbs /ipk xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
if /i %sku%==professional cscript %windir%\system32\slmgr.vbs /ipk xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
if /i %sku%==ultimate cscript %windir%\system32\slmgr.vbs /ipk xxxxx-xxxxx-xxxxx-xxxxx-xxxxx




제가 활용하는 수준은 딱 이정도이고 나머지 FOR 옵션에 대해서는 잘 모르겠습니다. 그냥 한번 읽어봤을 때 웬지 제가 사용할 일이 없을 것 같은 옵션들은 이해하려는 노력을 하지 않게 되더군요. 그리고 이 FOR 구문은 그 자체보다 괄호 안에, DO 뒤에 무엇을 넣어주느냐에 따라 가치가 결정된다고 생각합니다. 보통 IF와 SET 구문을 곁들여서 활용하니 IF와 SET에 대해서도 사용법을 익혀두시면 좋을 것 같습니다.


출처 : http://snoopybox.co.kr/1366

Posted by penguindori
카테고리 없음2011. 3. 22. 11:09
예전부터 한번 정리하려고 했는데 시간이 많이 걸리는 일이라 차일피일 미루다 오늘에야 올려봅니다. 처음에는 간단히 작성하려고 했는데 쓰다보니 글이 꽤 길어지더군요. 관심있는 분들만 읽어보세요. 여기서 다루는 내용을 완벽히 이해하시면 배치파일의 고수까지는 아니더라도 적어도 제가 활용하는 만큼은 만드실 수 있을 겁니다.



도움말 활용하기

일단 잘 모르는 명령어가 있다면 도움말을 통해 기능을 익혀야겠지요. 사실 저는 이 배치파일을 거의 도움말을 통해 혼자서 익혔습니다. 예전에 어떤 분이 댓글로 저보고 배치파일을 진짜 잘 짠다고 하셨는데, 저도 어차피 도움말 보면서 익힌 것이니까 누구든지 도움말을 잘 이해하면 충분히 훌륭한 배치파일을 만들 수 있다고 생각합니다. 그런데 경우에 따라 도움말이 별로 친절하지 않을 수도 있습니다. 하지만 처음에는 이해되지 않던 말들도 시간이 지나고 경험이 쌓이면 나중에 다시 봤을 때 이해되는 경우가 많습니다. 예전에는 제가 FOR 구문의 대체 변수 참조 확장을 잘 이해하지 못했는데, 최근에는 잘 이해하게 되었습니다. 여러분도 이 도움말을 적극적으로 활용해보세요.

일반적으로 해당 명령어 뒤에 /? 를 붙이면 도움말이 나옵니다. 예를 들어 FOR 반복문의 사용법이 궁금하다면 FOR /? 해보면 되겠지요. 또는 HELP FOR 하셔도 동일한 결과를 얻을 수 있습니다. 그런데 도움말이 길면 스크롤 압박이 있으니 이 경우 도움말을 텍스트 파일로 저장해서 보셔도 되겠습니다. 뒤에 > 파일.txt 적어주시면 출력 결과를 텍스트 파일로 저장해줍니다.

예제)
FOR /? > "FOR 사용법.TXT"



ECHO

ECHO는 글자 그대로 메아리를 뜻하는데, 화면에 메시지를 그대로 출력해줍니다. 배치파일에선 사용자에게 뭔가 설명해줘야 하는 부분이 많은데 이럴 때 ECHO를 활용합니다. 주로 SET, IF, GOTO 등과 결합해서 메뉴를 보여줄 때 많이 사용하지요.
 
예제)
ECHO 저는 에코입니다. 화면에 메시지를 보여주기 위해 사용되지요.

그런데 보통 배치파일 첫 줄에는 @ECHO OFF를 많이 적어줍니다. 만약 @ECHO OFF를 적어주지 않으면 각 명령줄을 화면에 그대로 한번 출력을 해주고 실행을 하기 때문에 화면이 많이 지저분해집니다. 그래서 보통은 배치파일의 첫 줄에서 ECHO를 OFF 시켜줍니다.

이 ECHO 명령어 역시 출력 결과를 텍스트파일로 저장할 수 있습니다. (리디렉션은 거의 모든 경우에 사용할 수 있습니다.) 예를 들어 DISKPART를 사용하는 경우 배치파일에선 DISKPART 다음줄부터 작동하지 않는데요, 이럴 때는 DISKPART 다음줄부터 사용될 명령어를 스크립트로 저장한 다음 DISKPART /S 스크립트 이렇게 사용해야 합니다.

예제)
@echo off
pushd %~dp0
echo create vdisk file=d:\test.vhd type=expandable maximum=20480 > test.txt
echo attach vdisk >> test.txt
echo create partition primary  >> test.txt
echo format quick fs=ntfs >> test.txt
echo assign letter >> test.txt
echo exit >> test.txt
diskpart /s test.txt
exit


위 예제가 무슨 소린지 아직은 잘 이해되지 않을 수도 있는데, 아무튼 echo와 리디렉션을 활용하여 텍스트파일로 출력하는게 가능하기 때문에 배치파일 안에서 이렇게 새로운 배치파일이나 스크립트를 echo만을 사용하여 작성할 수 있습니다. 위 예제는 윈도우 7에서 D 드라이브에 TEST.VHD 파일을 자동으로 생성하는 기능을 합니다.


※ ESCAPE 문자
제가 한가지 빠트려서 보충합니다. echo로 리디렉션을 할때 만약에 >> 기호 역시도 echo의 일부분으로 포함시키고 싶다면 어떻게 해야 할까요? 예를 들어

echo dir /b >> list.txt >> test.txt

위 구문을 실행하면 test.txt에 dir /b 부분만 들어갑니다. 넣고싶은 문자열은 dir /b >> list.txt 인데 말이죠. 이런 경우 앞서 나온 >> 문자열을 리디렉션이 아니라고 Escape 시킬 필요가 있습니다. 이때는 caret 문자 ^ 를 붙여주면 됩니다. 그러니까 위 예제를 결과값이 의도대로 나오게 고치려면

echo dir /b ^>^> list.txt >> test.txt



PUSHD %~DP0

이것 역시 제가 배치파일 초반에 항상 적어주는데요, 그 이유는 윈도우 7 때문입니다. 윈도우 7에선 특이하게도 UAC가 켜져있는 환경에서 배치파일을 관리자 권한으로 실행하는 경우 명령 프롬프트 위치가 C:\Windows\System32 기준으로 실행됩니다. 이게 왜 문제가 되냐하면 보통은 배치파일과 함께 같은 위치에 첨부 파일을 두고 다른 곳으로 복사해주는 명령어를 쓰거나 아니면 그 첨부파일을 실행하는 경우가 많은데, 이렇게 명령 프롬프트 위치가 변경되어 버리면 복사할 파일이나 실행할 파일의 절대경로를 적어야 합니다. 그런데 배치파일은 어느 위치에서 실행될지 아무도 모르죠. 따라서 복사할 파일의 절대경로도 그때그때 달라지게 됩니다. 물론 각 파일 경로 앞에 %~DP0를 일일이 붙여주면 되지만 그건 너무 귀찮은 일입니다. 따라서 이렇게 명령 프롬프트 위치를 배치파일이 있는 디렉터리로 되돌려주는게 훨씬 편합니다.

그런데 %~DP0의 의미가 궁금하실 것 같습니다. 이게 바로 FOR 구문의 대체 변수 참조 확장입니다. 자세한 내용은 FOR /? 입력해서 확인해보시구요, 저는 간단히 예제를 통해 설명드리겠습니다.

배치파일에서 %0는 그 배치파일의 전체 경로를 지칭합니다. %0 사이에 들어있는 ~dp는 %변수를 드라이브 문자와 경로로만 확장해줍니다. 예를 들어 제 컴퓨터 바탕화면에 예제.cmd 파일이 있는 경우

%0

“C:\Users\snoopy\Desktop\예제.cmd”

%~0

C:\Users\snoopy\Desktop\예제.cmd

%~d0

C:

%~p0

\Users\snoopy\Desktop\

%~n0

예제

%~x0

.cmd

%~dp0

C:\Users\snoopy\Desktop\


따라서 pushd %~dp0 라고 적으면 명령 프롬프트가 배치파일이 존재하는 위치로 돌아오게 됩니다. 이제 응용력이 뛰어나신 분들은 이런 생각을 하실 것 같습니다. "그렇다면 %~dpnx0 와 %~0 는 같은 값을 나타내겠군!" 네 그렇습니다. %~ 확장은 이처럼 한꺼번에 섞어서 사용해도 됩니다.

※ 참고로 PUSHD %~DP0 대신에 CD /D %~DP0 해도 상관은 없습니다.



TITLE

이건 별로 중요하지 않은데, 배치파일을 실행했을 때 콘솔창에 표시되는 창 제목을 지정합니다. 원래 아무것도 지정하지 않으면 제목이 C:\Windows\System32\cmd.exe 인데, 이 부분을 변경하는 것입니다.

예제)
title 아이폰 4 AVI, MKV 동영상 자동 변환기




MODE

이것도 별로 중요하지는 않은데, 배치파일에선 콘솔창 크기를 지정하는 용도로 사용할 수 있습니다.

예제)
mode con cols=40 lines=11





COLOR

콘솔창의 화면 색을 지정할 수 있습니다. 저는 까만 바탕에 흰 글씨가 마음에 들지만 예를 들어 파란 바탕에 흰 글씨로 지정할 수도 있겠지요.

   0 = 검정색       8 = 회색
   1 = 파랑색       9 = 연한 파랑색
   2 = 초록색       A = 연한 초록색
   3 = 옥색         B = 연한 옥색
   4 = 빨강색       C = 연한 빨강색
   5 = 자주색       D = 연한 자주색
   6 = 노랑색       E = 연한 노랑색
   7 = 흰색         F = 밝은 흰색

첫째 자리는 배경색이고 둘째 자리는 문자색입니다.

예제)
color 1F





CLS

화면을 깨끗하게 비웁니다. Clear Screen의 약자인데 보통 새로 메뉴를 출력해주는 경우 기존에 보이던 결과물 때문에 지저분하니까 CLS를 적어줍니다.



PAUSE

이것도 주로 안내문을 보여준 다음 적어줍니다. 일시정지 시켜둬야 사람들이 안내문을 읽어볼 수 있으니까요. 아무 키나 누르면 다음 명령어로 넘어갑니다. 저는 보통 배치파일을 작성하면서 중간중간에 PAUSE를 걸어넣고 파일을 실행해봅니다. 여기까지 오류없이 잘 작성되었는지 테스트 해보기 위함이죠.



IF, IF NOT, IF EXIST, IF ERRORLEVEL, ELSE

지금부터가 진짜 배치파일입니다. 위에서 다뤘던 내용들은 그냥 준비운동 정도였고 배치파일에서 가장 많이 사용되는 (저만 가장 많이 사용하는지도 모르지만 ;;;) 구문이 바로 IF, FOR, SET 입니다. 이 3가지 구문이 배치파일의 꽃이라고 저는 생각합니다. 특히 저는 FOR 반복문을 매우 자주 사용하는 편인데, FOR 구문을 잘 활용하시면 "감히 배치파일로 이게 가능한가?" 생각했던 기능들도 훌륭히 만들어낼 수 있습니다.

IF는 말 그대로 "~인지 아닌지" 를 판단합니다. 만약 조건이 참이면 그 뒤에 나오는 명령어를 수행하고 참이 아니면 다음 줄로 넘어갑니다.

IF 문자열1==문자열2 명령어

예제)
set A=snoopy
if %A%==snoopy echo %A%는 snoopy와 같습니다.
set B=snoopybox
if %B%==snoopy echo %B%는 snoopy와 같습니다.


위 예제에서 첫번째 IF 구문은 조건이 참이기 때문에 "snoopy는 snoopy와 같습니다." 문구를 출력해줍니다. 하지만 두번째 IF 구문은 조건이 거짓이기 때문에 그 뒤에 나오는 echo 명령어는 무시됩니다.

IF 뒤에 /i 를 붙이시면 대소문자를 구분하지 않습니다. 이게 윈도우에선 IF 도움말에 잘못 표기되어 있네요. /i를 붙이면 대소문자 구분이 없고, 안 붙이면 대소문자를 구분합니다. 도움말에는 반대로 나와 있습니다.


숫자 비교에는 아래 비교연산자들이 사용됩니다.

    EQU - 같음
    NEQ - 같지 않음
    LSS - 보다 작은
    LEQ - 작거나 같음
    GTR - 보다 큰
    GEQ - 크거나 같음

예제)
if 5 equ 5 echo 5는 5와 같습니다.
if 5 neq 5 echo 5는 5와 같지 않습니다.
if 5 lss 5 echo 5는 5보다 작습니다.
if 5 leq 5 echo 5는 5보다 작거나 같습니다.
if 5 gtr 5 echo 5는 5보다 큽니다.
if 5 geq 5 echo 5는 5보다 크거나 같습니다.


위 예제에서는 1, 4, 6번째 줄만 참이기 때문에 뒤에 있는 echo가 출력되겠지요? 대충 저렇게 쓴다는 것만 알고 다음으로 넘어갑니다.


IF NOT은 IF와 반대로 생각하면 됩니다. 뒤에 나온 조건이 거짓인 경우에 IF NOT은 참이 되기 때문에 명령을 실행하게 됩니다.

예제)
if not snoopy==snoopy echo 과연 이 메시지가 출력될까?

위 예제를 실행하면 echo 다음에 나오는 메시지가 출력되지 않습니다. IF NOT이니까요. 하지만 만약에 if not snoopy==snoopybox 했다면 뒤에 나오는 명령어는 출력이 되었겠지요?


IF EXIST는 지정한 파일, 폴더가 존재하면 조건을 참으로 받아줍니다. 대략 아래와 같은 예를 만들어 보겠습니다.

예제)
if exist %windir%\syswow64 echo 이 윈도우는 64비트입니다.
if not exist %windir%\syswow64 echo 이 윈도우는 64비트가 아닙니다.


위 예제를 실행하면 64비트 윈도우인 경우 첫번째 echo 구문만 출력될 것이고, 32비트 윈도우인 경우 두번째 echo 구문만 출력될 것입니다. 마찬가지로 IF EXIST 구문도 IF NOT EXIST로 활용할 수 있습니다.


IF ERRORLEVEL 역시 제가 자주 사용하는 구문인데 마지막으로 실행된 프로그램의 종료코드가 무엇인지를 가지고 조건을 판단합니다. 일반적으로 아무런 오류가 없이 잘 실행된 경우에는 종료코드가 0입니다. 오류가 발생하면 종료코드는 1 이상입니다. IF ERRORLEVEL 1  하시면 종료코드가 1보다 크거나 같은 경우 참으로 인식합니다.

예제)
ren C:\Windows WindowsXP
if errorlevel 1 echo 윈도우 폴더는 이름을 변경할 수 없어요.


위 예제에서 첫번째 줄에 Windows 폴더 이름을 WindowsXP로 변경하라고 합니다. 말이 안 되죠? 당연히 오류가 발생할 수 밖에 없습니다. 따라서 두번째 줄에 있는 IF ERRORLEVEL 1 구문은 바로 위에서 오류가 발생했기 때문에 값이 참으로 지정되어 echo 뒤에 나오는 문구를 출력해줍니다.


ELSE는 IF랑 묶어서 사용하는데 여기서부터는 괄호를 사용하겠습니다. 이 괄호를 보기좋게 잘 써야 프로그램을 체계적으로 짤 수 있고, 나중에 자기가 봐도 흐름이 한 눈에 보입니다. 위에서 예를 들었던 윈도우 64비트 판별을 ELSE로 표현해보자면

예제)
if exist %windir%\syswow64 (
          echo 이 윈도우는 64비트입니다.
) else (
          echo 이 윈도우는 64비트가 아닙니다.
)


그러니까 ELSE는 IF 조건이 거짓인 경우 실행됩니다. IF가 옳으면 IF 구문 뒤에 있는 명령어가 실행되고, 그게 아니면 ELSE 뒤에 있는 명령어가 실행됩니다. 둘중에 하나만 무조건 실행되게 만드는 양자택일 구문이라 보시면 됩니다.


여기까지 대충 IF 구문에 대해 알아봤는데요 실전 예제 몇가지를 소개해봅니다. 아래는 제가 아이폰 4 AVI, MKV 동영상을 MP4로 한방에 변환하기 글에서 사용한 SRT 자막 통합 스크립트입니다.


예제)
set list=자막 통합에 실패한 파일 목록.txt
if exist *.srt (
     if not exist temp md temp
     for %%a in (*.srt) do (
          if exist "%%~na.mp4" (
               Tools\mediainfo "%%~na.mp4" > temp\temp1.txt
               findstr /c:"Codec ID                         : tx3g" temp\temp1.txt > nul
               if errorlevel 1 (
                    Tools\mp4box -add "%%a":lang=ko:hdlr=sbtl "%%~na.mp4"
                    if errorlevel 1 echo %%~na.mp4 >> "%list%"
               ) else (
                   Tools\mp4box -add "%%~na.mp4"#video -add "%%~na.mp4"#audio -add "%%a":lang=ko:hdlr=sbtl "%%~na-new.mp4"
                   if errorlevel 1 echo %%~na.mp4 >> "%list%"
              )
         )
    )
)



list 라는 변수에 "자막 통합에 실패한 파일 목록.txt" 문자열을 할당하라
현 위치에 확장자 SRT 파일이 존재하면 (
     TEMP 폴더가 없다면 TEMP 폴더를 만들어라
     모든 SRT 파일에 대하여 다음 반복문을 실행하라 (
          만약 SRT 파일과 동일한 이름의 MP4 파일이 존재한다면 (
               미디어인포 프로그램으로 MP4 파일의 정보를 TEMP 폴더의 TEMP1.TXT 파일로 저장하라
               TEMP 폴더의 TEMP1.TXT 파일에서 Codec ID : tx3g 문구가 있는지 찾아봐라
               만약 이 문구가 없다면 (
                    기존에 있던 MP4 파일에 자막만 통합하라
                    만약 자막 통합시 오류가 발생했다면 MP4 파일 이름을 LIST 파일에 저장하라
               ) 만약 이 문구가 있다면 (
                    기존 MP4 파일에서 영상과 음성을 추출하고 자막을 통합하여 새로운 MP4 파일을 만들라
                    만약 자막 통합시 오류가 발생했다면 MP4 파일 이름을 LIST 파일에 저장하라
               )
          )
     )
)


말로 설명하려니 좀 지저분한데 아무튼 위 예제에는 IF, IF NOT, IF EXIST, IF ERRORLEVEL, ELSE 모두 다 적절히 잘 활용되어 있습니다.

한가지 예를 더 들어보겠습니다. 한글, 영어, 통합 자막 이렇게 3가지 자막을 통합하려 한다면 경우의 수는 8가지가 나오겠지요. 그런데 한글 자막은 무조건 존재하고, 통합한다고 가정하면 4가지 경우가 발생합니다.

한글

영어

통합

O

O

O

O

O

X

O

X

O

O

X

X



if exist 한글 (
          if exist 영어 (
                    if exist 통합 (
                              한글, 영어, 통합 모두 있는 경우 명령어
                    ) else (
                              한글, 영어만 있는 경우 명령어
                    )
          ) else (
                   if exist 통합 (
                              한글, 통합만 있는 경우 명령어
                   ) else (
                              한글만 있는 경우 명령어
                   )
          )
)


이정도면 IF 구문에 대해서는 충분히 설명을 했다고 생각되는데 어떠실지 모르겠네요. 프로그램을 작성하는 데 있어서 가장 중요한 것은 역시 논리적 사고라 생각합니다. 일단 논리적으로 설계도만 잘 짜면 코딩은 껍데기에 불과하다고 봅니다. 위 예는 아주 간단한 배치파일에 불과하지만 엄청 큰 규모의 프로그램을 작성하려면 설계도부터 체계적으로 잘 짜야겠지요.



FOR

FOR 구문은 여기서 따로 다루지 않겠습니다. 지난번에 작성했던 글을 참조하세요.
2010/08/28 - [윈도우 일반] - FOR 구문 기초 사용법

Perl 스크립트에는 FOR, FOREACH, WHILE 반복문이 있던데, 배치파일에는 FOR 반복문밖에 없고 사용법도 조금 제한적이라 아쉽기는 합니다. 하지만 그래도 저는 FOR 구문이 배치파일의 꽃중의 꽃이라 생각합니다. 꼭 잘 익혀두셔서 능숙하게 다루실 수 있기를 빌겠습니다.



SET

SET에 대해서는 사실 프로그래밍 언어를 좀 알아야 개념을 이해하는데 도움이 되는데, 뭐 저도 프로그래밍 언어는 모르니까 딱 제가 이해하는 수준 정도로만 설명을 드리겠습니다.

SET 변수=문자열

일단 이게 기본입니다. 왼쪽에는 변수 이름을 적어주고 오른쪽에는 변수의 값을 적어줍니다. 그리고 배치파일에서 변수를 호출할 때는 양쪽에 %를 붙여주시면 됩니다.

예제)
set A=snoopy
echo %A%


위 예제를 실행하면 화면에 snoopy를 출력해줍니다. A 라는 변수에 snoopy라는 문자열이 값으로 지정되었기 때문입니다. 이때 변수는 대문자와 소문자를 구분합니다. 즉 A와 a는 다른 변수입니다.

SET /A 변수=수식

변수에 숫자를 할당할 때는 그냥 할당하면 되지만, 수식의 계산 결과를 할당하려면 /A 옵션을 붙여야 합니다.

예제)
set B=5
set /a B=B+3
echo %B%


위 예제의 결과값은 8이 나오겠지요? 참고로 연산식은 아래와 같이 좀 더 간단히 표현할 수 있는데

    = *= /= %= += -=    - 할당

예를 들어 위에 있는 set /a B=B+3 대신에 set /a B+=3 이렇게도 쓸 수 있습니다.
좀 더 예를 들어 설명드리는게 이해가 쉽겠네요.

B는 5라고 가정하고 아래 예제는 각각

set /a B+=3          =>   B에 3을 더해서 새로운 B 값을 할당          => 8
set /a B-=3          =>   B에 3을 빼서 새로운 B 값을 할당          => 2
set /a B*=3          =>   B에 3을 곱해서 새로운 B 값을 할당          => 15
set /a B/=3          =>   B를 3으로 나눈 몫을 새로운 B 값에 할당          => 1
set /a B%=3         =>   B를 3으로 나눈 나머지를 새로운 B 값에 할당          => 2


아래 예제의 결과물을 예측해보세요.

예제)
set i=0
for /l %%a in (1,1,100) do (
     set /a i += %%a
)
echo %i%
pause
exit


정답은

더보기



다음은 메뉴 고르기에 많이 사용되는 SET /P 구문입니다. /p를 붙이면 프롬프트 상에서 사용자에게 입력값을 받는데요, C 언어의 scanf 함수와 비슷하다 볼 수 있습니다. 다음 예제를 배치파일로 실행해보면 이해가 바로 될 겁니다.

예제)
@echo off
set /p age=당신의 나이를 입력해주세요 :
echo 당신의 나이는 %age% 입니다.
pause
exit



변수 확장에 관한 이야기도 있는데 이건 제가 별로 사용하지 않아서... 대략 예를 하나만 들어보겠습니다.

예제)
@echo off
set snoopy=스누피는 컴퓨터를 좋아합니다.
set snoopy=%snoopy:~-10%
echo %snoopy%


위 예제를 실행하면 "퓨터를 좋아합니다." 문자만 출력됩니다. ~-10 이게 "뒤에서 10개의 문자만" 추출하는건데 아무튼 이 확장에 대해서는 그냥 각자 도움말을 참조하여 공부하세요.



SETLOCAL

이놈은 위 SET 명령어와 같이 활용해야 하는데 저는 잘 쓰지 않습니다. 그런데 이게 필요한 경우가 있습니다. 그것은 바로

SETLOCAL ENABLEDELAYEDEXPANSION

이녀석은 제가 가끔 FOR 구문에서 사용하는데 사실 저도 100% 완벽히 이해한 것은 아닙니다. 일단 아래 예를 한번 보세요.

예제)
for %%a in (C: D: E: F: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: W: X: Y: Z:) do (
     set b=%%a
     echo %b%
)


얼핏 생각하기에 위 예제는 작동할 것 같은데요, 사실은 작동하지 않습니다. 첫 명령어에서 b 변수에 C: 문자열이 할당되니까 C: 를 출력해주고, 그 다음엔 다시 b에 D: 문자열이 할당되어서 D: 문자열을 출력해주고... 이걸 Z: 출력까지 반복할 것 같지만 이상하게도 위 구문은 실행되지 않습니다.

위 예제가 의도대로 구동되도록 하려면 아래와 같이 바꿔야 합니다.

SETLOCAL ENABLEDELAYEDEXPANSION

for %%a in (C: D: E: F: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: W: X: Y: Z:) do (
     set b=%%a
     echo !b!
)


그러니까 SETLOCAL ENABLEDELAYEDEXPANSION을 설정한 다음 %b% 대신 !b!를 사용해야 합니다. 자세한 개념은 저도 설명을 못 하니 그냥 그렇다고만 알아두세요. 제가 대충 이해하기로는 이렇습니다. 저렇게 FOR 이나 IF에서 괄호를 사용해 아래쪽에 명령줄을 여러개 써넣어도 사실은 이게 한줄짜리 명령어인데, 명령어를 읽어들이는 과정과 명령어를 실행하는 과정 사이에 발생하는 시차 때문에 이런 문제가 생깁니다. b라는 변수에는 아무런 값도 할당되지 않은 상태인데 그상태로 명령어가 입력되기 때문에, 저 반복문에서는 "echo 설정되어있지 않은 변수" 이걸 반복하게 되는거죠. set으로 %%a 값을 b에 넣어주는건 시간적으로 그 다음에 일어나는 일이구요. 그래서 SETLOCAL ENABLEDELAYEDEXPANSION 옵션이 존재합니다. 아무튼 % 대신 ! 쓴다는거 알아두세요.



GOTO

이것도 많이 쓰이는데 사용법이 어렵지 않아서 간단히 다룹니다. GOTO 뒤에는 레이블을 적어주시면 그리로 이동하는데 레이블은 콜론으로 시작합니다.

예제)
:menu
cls
echo.
echo 1. 저는 남자입니다.
echo 2. 저는 여자입니다.
echo.
set /p menu= 번호를 선택해주세요 :
if "%menu%"=="1" goto male
if "%menu%"=="2" goto female
goto menu

:male
cls
echo.
echo 당신은 남자입니다.
echo.
pause
exit

:female
cls
echo.
echo 당신은 여자입니다.
echo.
pause
exit


위 예에서 set /p menu 값에 1을 입력하면 male 부분으로 이동하고 2를 입력하면 female 부분으로 이동합니다. 1,2가 아닌 엉뚱한 값을 입력하면 menu 부분으로 다시 이동합니다. GOTO는 어렵지 않죠?

그리고 제가 exit를 썼는데 :EOF를 쓰기도 합니다. GOTO :EOF라고 적어주면 exit와 같은 효과를 얻을 수 있습니다. 따로 :EOF 레이블을 만들어줄 필요는 없습니다.



실전 예제 고스트 자동 복구 DVD

지금까지 지루한 내용을 꾹 참고 다 읽어오신 분이라면 (FOR 구문까지) 이제 웬만한 배치파일은 작성하실 수 있을 것 같은데, 그래도 이게 눈으로 보는 것과 직접 실습하는 것은 많이 다릅니다. 컴퓨터 뿐 아니라 공부나 다른 일도 마찬가지인데, 직접 몸으로 느껴봐야 진짜 나만의 지식이 될 수 있습니다.

일단 이 글을 작성한 이유가 고스트 자동 복구 DVD 만들기 글에서 배치파일 소스를 알려달라는 분들이 많았기 때문인데요, 저 글에 나온 배치파일은 이 글에서 설명한 어려운 구문들이 거의 들어가지 않습니다. 정말 쉽고 간단한 구문으로만 이루어져 있습니다. 그런데 당시 작성했던 스크립트를 다 쓰려면 좀 길어지기 때문에 메뉴를 단순화 해서 4가지만 적어보겠습니다.

@echo off
for %%a in (C: D: E: F: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: W: X: Y: Z:) do (
          if exist %%a\sources\backup.gho set DVD=%%a
)

:_main
cls
echo.
echo.
echo.
echo          1. 고스트 수동으로 작업하기
echo.
echo          2. 첫번째 하드의 첫번째 파티션에 복원하기
echo.
echo          3. 명령 프롬프트 띄우기
echo.
echo          4. 파티션 위저드 실행하기
echo.
echo.
echo.
set /p menu=번호를 입력해주세요 :
if "%menu%"=="1" goto _ghost
if "%menu%"=="2" goto _restore
if "%menu%"=="3" goto _cmd
if "%menu%"=="4" goto _pwhe
goto _main

:_ghost
X:\ghost32
goto _main

:_restore
X:\ghost32 -clone,mode=pload,src=%DVD%\sources\backup.gho:1,dst=1:1 -auto -sure -rb -noide -fnf
exit

:_cmd
start cmd
goto _main

:_pwhe
X:\pwhe
goto _main




어떤가요? 이 글에서 설명한 복잡한 내용은 하나도 없고 완전 단순하죠? 위 예제를 응용해서 나만의 고스트 자동 복구 DVD를 만드시면 되겠습니다.


혹시 잘못된 내용 있으면 고수님들 지적 부탁드립니다. ^^

출처 : http://snoopybox.co.kr/1404
Posted by penguindori
DataBase/Oracle2011. 3. 21. 21:04
SELECT parameter,value FROM nls_database_parameters
Posted by penguindori