윈도우 (2)

안녕하세요, 케이치입니다.


오늘도 쉽게 넘어가지를 않고 에러가 발생하네요. 오늘은 윈도우용 도커를 설치를 했다가 jre 8 이 정상적으로 동작을 하지 못하게 된 케이스입니다.


jdk7, 8, jre7, 8을 윈도우10 에 설치해서 사용중인 환경인데요 jdk8을 설치해보신 분들은 아시겠지만 7버전 까지는 c:\Program Files\Java 밑에 설치가 되고 환경설정 변수에도 해당 경로의 bin디렉토리를 설정해줬었는데(C:\Program Files\Java\jre7\bin), 8버전부터는 C:\ProgramData\Oracle\Java\javapath\java.exe 를 환경변수에 가지도록 해놨더군요. 물론 설치 경로는 Program Files\Java\jdk1.8 이었는데도 불구하고 말이죠. 어찌되었건 잘 쓰고있었는데, 문제가 생긴건 챗봇 오픈소스 테스트를 하려고 윈도우용 도커를 설치해서 테스트를 하고난 뒤에 발생했습니다. 갑자기 이클립스(Oxygen)가 실행이 안되더군요. 

뭐지? 뭐지? 하다가, 이클립스를 다시 설치해보고 실행했더니 처음 한번은 잘 됩니다. 다시 이클립스를 종료했다가 실행시키면 또 안되고 참 희한한 현상이 발생했습니다. ㅠㅠ


아무튼 한참을 해메다가 neon 버전으로 다시 설치해서도 해보고 하다가 java를 실행해봤는데 아래와 같은 에러가 발생하더군요.


Error occurred during initialization of VM

java/lang/NoClassDefFoundError: java/lang/Object


허허, 이런 메시지는 또 처음 봐서 구글링을 해봤더니 jdk, jre 설치가 정상적으로 안된 케이스에서 발생하는 경우가 많은 것 같더군요.


조금 전까지 아무 문제 없던게 도커를 설치했더니 자바가 실행이 안된다? 좀 이해가 안되는 부분이라서 java명령어를 실행할 때 Program Files 하위의 java를 쓰는건지 ProgramData하위의 java를 쓰는건지 확인해보고 싶었습니다. 그때 사용한 명령어는 아래와 같습니다.


 for %i in (java.exe) do @echo. %~$PATH:i


윈도우에서 기본적으로 제공해주는 for 명령어를 이용한 것으로 도움말을 출력해보면 아래와 같습니다.



C:\Users\dev>for /?

파일 집합에서 각 파일에 대해 지정된 명령을 실행합니다.


FOR %변수 IN (집합) DO 명령어 [명령어 매개 변수]


  %변수      바꿀 수 있는 매개 변수를 한 문자로 지정합니다.

  (집합)      하나 이상의 파일을 지정합니다. 와일드카드를 사용할 수 있습니다.

  명령어      각 파일에 대해 수행할 명령을 지정합니다.

  명령어-매개 변수

              지정된 명령의 매개 변수나 스위치를 지정합니다.


일괄 프로그램에서 FOR 명령을 쓰려면, '%변수' 대신 '%%변수'를 지정하십시오.

변수 이름에서는 대문자와 소문자를 구별하므로 %i와 %I는 다릅니다.


명령 확장을 사용하면 FOR 명령에 아래와 같은 추가적인 형태가

지원됩니다.


FOR /D %변수 IN (집합) DO 명령 [명령-매개 변수]


   집합에 대표 문자가 있으면 파일 이름 대신 디렉터리 이름과

   일치하도록 지정합니다.


FOR /R [[드라이브:]경로] %변수 IN (집합) DO 명령 [명령-매개 변수]


   [드라이브:]경로를 루트로 하여 디렉터리 트리를 따라 내려가며

   FOR 구문을 트리의 각 디렉터리에서 실행합니다. /R 스위치 뒤에

   디렉터리가 지정되지 않으면 현재 디렉터리가 사용됩니다.

   집합에 마침표(.)가 사용되면 디렉터리 트리만 나열합니다.


FOR /L %변수 IN (시작,단계,끝) DO 명령 [명령-매개 변수]


   집합은 단계별로 증가/감소하는 시작부터 끝까지의 일련의 숫자입니다.

   따라서 (1,1,5)는 1 2 3 4 5를 나타내며 (5,-1,1)은 5 4 3 2 1을

   나타냅니다.


FOR /F ["옵션"] %변수 IN (파일-집합) DO 명령 [명령-매개 변수]

FOR /F ["옵션"] %변수 IN ("문자열") DO 명령어 [명령-매개 변수]

FOR /F ["옵션"] %변수 IN ('명령어') DO 명령어 [명령-매개 변수]


    또는 usebackq 옵션이 있는 경우:


FOR /F ["옵션"] %변수 IN (파일-집합) DO 명령 [명령-매개 변수]

FOR /F ["옵션"] %변수 IN ('문자열') DO 명령어 [명령-매개 변수]

FOR /F ["옵션"] %변수 IN (`명령어`) DO 명령어 [명령-매개 변수]


   파일-집합은 하나 이상의 파일 이름입니다. 파일-집합의 각 파일은

   다음 파일로 이동하기 전에 열기 또는 읽기 등의 작업이 진행됩니다.

   파일을 읽어서 문자열을 한 행씩 분리하고 각 행을 0개 이상의

   토큰으로 구문 분석하는 과정으로 되어 있습니다. For 루프의 본문은발견된 토큰 문자열에 설정된 변수 값(들)과 함께 호출됩니다.

   기본값으로 /F는 파일의 각 행으로부터 분리된 토큰을 첫 번째 공백에전달합니다. 빈 행은 건너뜁니다. "옵션" 매개 변수를 지정하여

   기본 구문 분석 동작을 무시할 수 있습니다. 이것은 다른 구문 분석

   매개 변수를 지정하는 하나 이상의 키워드를 갖는 인용 부호로

   묶인 문자열입니다.

   키워드는 아래와 같습니다.


   eol=c  - 행 끝 설명 문자를 지정합니다 (하나만)

   skip=n  - 파일의 시작 부분에서 무시할 행의 개수를 지정합니다.

   delims=xxx  - 구분 문자 집합을 지정합니다.  이것은 공백 또


계속하려면 아무 키나 누르십시오 . . .


너무 길어서 짧게 끊었습니다. 위에서 사용한 명령어는 유닉스의 which java와 같이 PATH환경변수에 있는 디렉토리 경로를 훑으면서 제일 처음 만나는 java위치를 출력해줍니다. 즉, 윈도우용 which 명령어라고 생각하시면 될 것 같습니다.



이상입니다.




아래는 기록을 위해서 개인적으로 작성했던 노트입니다.

오픈소스 챗봇 테스트를 위해서 Docker 설치하고 테스트 한 이후부터 이클립스 실행이 안되는 현상 발생
    - 다시 동일한 디렉토리에 Oxygen버전 인스톨러를 이용해서 재설치 한 뒤 launch하면 기동이 되나, 재부팅 후에는 역시 또 안됨
    - Neon 버전처럼 패키지로 배포되는 이클립스는 새로 다운받아서 실행해도 실행 안됨
    - Java를 실행해보니 다음과 같은 에러 메시지 발생
        - Error occurred during initialization of VM
          java/lang/NoClassDefFoundError: java/lang/Object
    - 현재 jdk1.7과 1.8 버전이 설치되어있고 jre역시 7, 8 버전이 설치되어있음
    - 아래 명령어를 이용하여 어느 위치의 자바가 실행되고 있는지 확인
        for %i in (java.exe) do @echo. %~$PATH:i
        - C:\ProgramData\Oracle\Java\javapath\java.exe
    - jdk 1.8을 설치하면 환경변수에 위 경로로 세팅이 됨
    - Docker를 설치하고나서 위 java를 실행하면 에러가 발생함
    - jdk1.7, jdk1.8 의 java.exe를 실행하면 정상적으로  실행이 되는 것을 확인
    - jdk1.8을 재설치하여 정상적으로 java가 실행이 되도록 할 수도 있으나 일단은 환경변수 확인
    - 환경변수에는 JAVA_HOME에 jdk1.8의 패스가 잡혀있고 PATH에 %JAVA_HOME%\bin이 있음, 그리고 ProgramData\OracleJava\javapath도 잡혀있음
    - 환경변수에서 ProgramData\OracleJava\javapath 경로는 제거함
    - 다시 이클립스를 재기동 해보니 정상적으로 기동함

    - jre1.8의 java는 정상적으로 실행이 안됨 -> 재설치






안녕하세요 케이치입니다.


오늘은 윈도우에서 특정 포트를 사용중인 프로세스를 죽이는 법을 가지고 왔습니다. 


최근에 Node.js 앱을 테스트해볼 일이 있었는데 고정적으로 특정 포트를 사용하는 앱이었습니다. 윈도우에서 cmd 창을 띄워서 실행시켰다가 Ctrl + C로  프로그램을 중지시켰는데 다시 기동시켰더니 포트가 이미 사용중이라면서 에러가 나더라구요. 그래서 이놈을 어떻게 죽여야 하나 찾아봤습니다.


우선 제 환경은 Windows 10이구요 CMD창을 띄우면 아래처럼 나옵니다.


제가 봤던 에러 메시지는 아래와 같구요


Error: listen EADDRINUSE :::2002


2002번 포트가 이미 사용중이라는 메시지입니다.


그럼 누가 이 포트를 사용중인지 찾아보죠.


cmd 창에 아래 명령어를 입력합니다.


C:\Users\dev>netstat -ano|find "2002"

  TCP    0.0.0.0:2002         0.0.0.0:0           LISTENING       5096

  TCP    [::]:2002              [::]:0                 LISTENING       5096


출력된 내용은 TCP 프로토콜에서 2002번 포트로 리스닝하고있는 프로세스의 ID가 5096번이라는 걸 말해줍니다. 우리는 5096 프로세스를 죽이면 되는거겠죠. 죽이기 전에 위 명령어에 대해서 설명을 좀 드리겠습니다.


netstat -ano는 netstat 명령어를 a, n, o 옵션으로 실행시키라는 의미죠. 여기서 a, n, o에 대한 설명은 netstat /? 를 입력하면 확인할 수 있습니다.


C:\Users\dev>netstat /?


프로토콜 통계와 현재 TCP/IP 네트워크 연결을 표시합니다.


NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-x] [-t] [interval]


  -a            모든 연결과 수신 대기 포트를 표시합니다.

  -b            각 연결 또는 수신 대기 포트 생성과 관련된 실행 파일을

                표시합니다. 잘 알려진 실행 파일이 여러 독립 구성 요소를

                호스팅할 경우 연결 또는 수신 대기 포트 생성과 관련된

                구성 요소의 시퀀스가 표시됩니다.

                이러한 경우에는 실행 파일 이름이 대괄호로 아래에

                표시되고 위에는 TCP/IP에 도달할 때까지

                호출된 구성 요소가 표시됩니다. 이 옵션은 시간이 오래

                걸릴 수 있으며 사용 권한이 없으면 실패합니다.

  -e            이더넷 통계를 표시합니다. 이 옵션은 -s 옵션과 함께

                사용할 수 있습니다.

  -f            외부 주소의 FQDN(정규화된 도메인 이름)을

                표시합니다.

  -n            주소와 포트 번호를 숫자 형식으로 표시합니다.

  -o            각 연결의 소유자 프로세스 ID를 표시합니다.

  -p proto      proto로 지정한 프로토콜의 연결을 표시합니다. proto는

                TCP, UDP, TCPv6 또는 UDPv6 중 하나입니다. -s 옵션과 함께

                사용하여 프로토콜별 통계를 표시할 경우 proto는 IP, IPv6, ICMP,

                ICMPv6, TCP, TCPv6, UDP 또는 UDPv6 중 하나입니다.

  -q            모든 연결, 수신 대기 포트 및 바인딩된 비수신 대기 TCP

                포트를 표시합니다. 바인딩된 비수신 대기 포트는 활성 연결과 연결되거나

                연결되지 않을 수도 있습니다.

  -r            라우팅 테이블을 표시합니다.

  -s            프로토콜별 통계를 표시합니다. 기본적으로 IP, IPv6, ICMP,

                ICMPv6, TCP, TCPv6, UDP 및 UDPv6에 대한 통계를 표시합니다.

                -p 옵션을 사용하여 기본값의 일부 집합에 대한 통계만

                지정할 수 있습니다.

  -t            현재 연결 오프로드 상태를 표시합니다.

  -x            NetworkDirect 연결, 수신기 및 공유 끝점을

                표시합니다.

  -y            모든 연결에 대한 TCP 연결 템플릿을 표시합니다.

                다른 옵션과 함께 사용할 수 없습니다.

  interval      다음 화면으로 이동하기 전에 지정한 시간(초) 동안 선택한 통계를 다시 표시합니다.

                통계 다시 표시를 중지하려면 <Ctrl+C>를 누르십시오.

                이 값을 생략하면 현재 구성 정보가

                한 번만 출력됩니다.


이렇게 실행된 결과를 파이프( | )를 통해 find 명령어로 전달하여 "2002"를 찾으라고 하고있죠. find 명령어는 리눅스의 grep과 비슷한 기능을 합니다.


find "2002"를 제외한 앞 부분만 실행시키면 2002포트 말고도  다른 여러 포트들에 대한 목록이 함께 출력됩니다. 


자, 그럼 5096번 프로세스를 죽여볼까요?


윈도우에서 프로세스를 죽일 때는 "작업관리자"를 이용하는 방법도 있지만 개발을 하다보면 간혹 작업관리자에서 찾기 힘든 녀석들이 있죠. 그럴 경우 cmd창에서 작업을 하면 훨씬 수월하게 작업을 할 수 있습니다.


프로세스를 죽일 때는 taskkill 명령어를 사용합니다. 그럼 이녀석에 대한 설명을 한번 볼까요?


C:\Users\dev>taskkill /?


TASKKILL [/S 시스템 [/U 사용자 이름 [/P [암호]]]]

         { [/FI 필터] [/PID 프로세스 id | /IM 이미지 이름] } [/T] [/F]


설명:

    이 도구는 프로세스 ID(PID) 또는 이미지 이름으로 작업을 종료하는 데

    사용합니다.


매개 변수 목록:

    /S    시스템           연결할 원격 시스템을 지정합니다.


    /U    [도메인\]사용자  명령을 실행해야 하는 사용자 컨텍스트를

                           지정합니다.


    /P    [암호]           해당 사용자 컨텍스트의 암호를 지정합니다.

                           생략한 경우에는 물어봅니다.


    /FI   필터             작업 집합을 선택하는 필터를 적용합니다.

                           "*"를 사용할 수 있습니다. 예: imagename eq acme*


    /PID  프로세스_ID      종료할 프로세스의 PID를 지정합니다.

                           TaskList를 사용하여 PID를 얻을 수 있습니다.


    /IM   이미지 이름      종료할 프로세스의 이미지 이름을

                           지정합니다. 와일드카드 문자 '*'를 사용하여

                           모든 작업 또는 이미지 이름을 지정할 수 있습니다.


    /T                     지정된 프로세스와 그 프로세스로부터 시작된

                           모든 자식 프로세스를 종료합니다.


    /F                     프로세스를 강제로 종료하도록 지정합니다.


    /?                     이 도움말 메시지를 표시합니다.


필터:

    필터 이름     유효한 연산자             유효한 값

    -----------   ---------------           -------------------------

    STATUS        eq, ne                    RUNNING |

                                            NOT RESPONDING | UNKNOWN

    IMAGENAME     eq, ne                    이미지 이름

    PID           eq, ne, gt, lt, ge, le    PID 값

    SESSION       eq, ne, gt, lt, ge, le    세션 번호.

    CPUTIME       eq, ne, gt, lt, ge, le    CPU 시간 형식

                                            hh:mm:ss

                                            hh - 시간,

                                            mm - 분, ss - 초

    MEMUSAGE      eq, ne, gt, lt, ge, le    메모리 사용(KB)

    USERNAME      eq, ne                    사용자 이름([domain\]user

                                            형식)

    MODULES       eq, ne                    DLL 이름

    SERVICES      eq, ne                    서비스 이름

    WINDOWTITLE   eq, ne                    창 제목


    참고

    ----

    1) /IM 스위치에 대한 와일드카드 문자 '*'는 필터가 적용될 때만

    사용할 수 있습니다.

    2) 원격 프로세스는 항상 강제적으로(/F) 종료될 수 있습니다.

    3) 원격 컴퓨터가 지정되면 "WINDOWTITLE"  및 "STATUS" 필터는

       지원되지 않습니다.


예:

    TASKKILL /IM notepad.exe

    TASKKILL /PID 1230 /PID 1241 /PID 1253 /T

    TASKKILL /F /IM cmd.exe /T

    TASKKILL /F /FI "PID ge 1000" /FI "WINDOWTITLE ne untitle*"

    TASKKILL /F /FI "USERNAME eq NT AUTHORITY\SYSTEM" /IM notepad.exe

    TASKKILL /S 시스템 /U domain\username /FI "USERNAME ne NT*" /IM *

    TASKKILL /S 시스템 /U 사용자 이름 /P 암호 /FI "IMAGENAME eq note*"


설명을 읽어보니 우리에게 필요한 내용이 너무나도 잘 설명되어있네요.


그럼 이제 프로세스를 죽여보겠습니다.


C:\Users\dev>netstat -ano|find "2002"

  TCP    0.0.0.0:2002           0.0.0.0:0              LISTENING       5096

  TCP    [::]:2002              [::]:0                 LISTENING       5096


C:\Users\dev>taskkill /pid 5096

오류: 프로세스(PID 5096)를 종료할 수 없습니다.

원인: 이 프로세스는 /F 옵션을 사용하여 강제로 종료해야 합니다.


C:\Users\dev>taskkill /pid 5096 /f /t

성공: PID 5096인 프로세스(PID 11172인 자식 프로세스)가 종료되었습니다.


보시면 중간에 오류가 한번 나면서 /F옵션을 쓰라고 나오네요. 참고에 원격 프로세스는 /F 옵션을 줘야 종료된다고 나와있는데 그거 때문인것 같습니다. 그래서 F옵션을 추가하고 혹시나 해서 자식프로세스까지 죽이는 T옵션을 추가했습니다. 아!! 그리고  옵션은 대소문자 구문을 하지 않습니다. 윈도우는 영어 대소문자 구분을 하지 않죠. 


이상입니다. 오늘은 여기까지 쓸게요. 오늘도 즐코하세요~