• SEARCH

    통합검색
세모계
    • Dark Mode
    • GNB Always Open
    • GNB Height Maximize
    • Color
    • Brightness
    • SINCE 2015.01.19.
    • 세모계 세모계
    •   SEARCH
    • 세상의 모든 계산기
      • 자유(질문) 게시판
      • 계산기 뉴스/정보
      • 수학, 과학, 공학 이야기
      • 세모계 : 공지 게시판
        • 구글 맞춤검색
    • TI
    • CASIO
    • HP
    • SHARP
    • 일반(쌀집) 계산기
    • 기타계산기
    • by OrangeDay
  • 세상의 모든 계산기 자유(질문) 게시판
    • 세상의 모든 계산기 자유(질문) 게시판 일반 ()
    • (Nginx) "웹 크롤러 request 순위 확인" 스크립트 (우분투)

    • Profile
      • 세상의모든계산기
      • 2025.02.15 - 10:57 2025.02.14 - 17:40 4208
    #!/bin/bash
    # monip.sh
    # Nginx access 로그에서 상위 25개 IP를 분석하여
    # - Bot (Crawler)인 경우: User-Agent 문자열로 Bot 종류를 추출
    # - User인 경우: whois 명령으로 Organization Name 조회
    # 그리고 IP별 국가 정보(geoiplookup)를 추가하여 출력함
    
    LOG_FILE="/var/log/nginx/access.log"
    
    if [ ! -f "$LOG_FILE" ]; then
        echo "로그 파일이 존재하지 않습니다: $LOG_FILE"
        exit 1
    fi
    
    awk '
      # Bot의 종류를 판별하는 함수
      function get_bot_type(ua,   ua_lower) {
        ua_lower = tolower(ua)
        if(ua_lower ~ /googlebot/)   return "Googlebot"
        if(ua_lower ~ /bingbot/)      return "Bingbot"
        if(ua_lower ~ /slurp/)        return "Yahoo Slurp"
        if(ua_lower ~ /duckduckbot/)  return "DuckDuckBot"
        if(ua_lower ~ /baiduspider/)  return "Baiduspider"
        if(ua_lower ~ /yandex/)       return "YandexBot"
        return "Unknown Bot"
      }
      
      # User-Agent 문자열을 바탕으로 사용자 종류를 판별
      function classify_user_agent(ua) {
        if(ua == "" || ua == "-") return "알 수 없음"
        ua_lower = tolower(ua)
        if(ua_lower ~ /bot|crawl|spider|slurp|mediapartners/) return "Bot (Crawler)"
        return "User"
      }
      
      # geoiplookup 명령을 사용하여 IP의 국가 정보를 가져오는 함수
      function get_country(ip,   cmd, result, arr, country) {
        cmd = "geoiplookup " ip " 2>/dev/null"
        if ((cmd | getline result) > 0) {
          # 예: "GeoIP Country Edition: US, United States"
          split(result, arr, ":")
          if(length(arr) > 1) {
            country = arr[2]
            gsub(/^[ \t]+|[ \t]+$/, "", country)
          } else {
            country = "Unknown"
          }
        } else {
          country = "조회 실패"
        }
        close(cmd)
        return country
      }
      
      {
        # Combined log format 예제:
        # 127.0.0.1 - - [14/Feb/2025:12:00:00 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 ..."
        regex = "([^ ]*) - ([^ ]*) \\[([^\\]]*)\\] \"([^\"]*)\" ([^ ]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\""
        if(match($0, regex, arr)) {
          ip      = arr[1]
          referer = arr[7]
          ua_str  = arr[8]
          
          count[ip]++                  # IP별 요청 횟수 집계
          ua_stats[ip, ua_str]++        # IP별 User-Agent 빈도 집계
          ref_stats[ip, referer]++      # (참고용) 리퍼러 집계
        }
      }
      
      END {
        # gawk의 asorti() 함수로 요청 횟수 기준 내림차순 정렬
        n = asorti(count, sorted, "@val_num_desc")
        
        # 헤더 출력 (순서: 순위 | IP | 국가 | Requests | 사용자 종류 | 기타정보)
        printf("| %-2s | %-15s | %-18s | %-10s | %-17s | %-21s |\n", "순위", "IP", "국가", "Requests", "사용자 종류", "기타정보")
        print("|------|-----------------|----------------------|------------|------------------------|-------------------------|")
        
        limit = (n < 25 ? n : 25)
        for(i = 1; i <= limit; i++){
          ip = sorted[i]
          req_count = count[ip]
          
          # 해당 IP의 가장 많이 등장한 User-Agent 선택
          max_ua = ""
          max_ua_count = 0
          for(key in ua_stats) {
            split(key, parts, SUBSEP)
            if(parts[1] == ip) {
              if(ua_stats[key] > max_ua_count) {
                max_ua_count = ua_stats[key]
                max_ua = parts[2]
              }
            }
          }
          
          user_type = classify_user_agent(max_ua)
          
          extra = ""
          if(user_type == "User") {
             # User인 경우: whois 명령으로 Organization Name 조회
             cmd = "whois " ip " 2>/dev/null | grep -i -m 1 \"orgname:\""
             org = ""
             if((cmd | getline org) > 0) {
                sub(/^[ \t]+/, "", org)
                split(org, a, ":")
                if(length(a) > 1) {
                  org = a[2]
                  gsub(/^[ \t]+/, "", org)
                }
             } else {
                org = "조회 실패"
             }
             close(cmd)
             extra = org
          } else if(user_type == "Bot (Crawler)") {
             extra = get_bot_type(max_ua)
          } else {
             extra = "없음"
          }
          
          # 각 IP의 국가 정보 추가
          country = get_country(ip)
          
          printf("| %-4d | %-15s | %-20s | %-10d | %-22s | %-25s |\n", i, ip, country, req_count, user_type, extra)
        }
      }
    ' "$LOG_FILE"

     

     

    사용 방법

    1. 스크립트를 파일에 저장 후 실행 권한 부여:

      chmod +x monip.sh

    2. 스크립트 실행 (필요한 경우 sudo로):

      sudo ./monip.sh

     

     

    결과

    image.png

    Attached file
    image.png 288.8KB 32
    이 게시물을..
    N
    0
    0
    • 세상의모든계산기 25
      세상의모든계산기

      계산기는 거들 뿐
      혹은
      계산기를 거들 뿐

    세상의모든계산기 님의 최근 글

    ban 설정 강화 4704 1 2026 05.09 정적분 구간에 미지수가 있고, solve 를 사용할 수 없을 때 그 값을 확인하려면? 1493 4 2026 04.10 높아질수록 좁아지는 시야에 대하여 - written by ChatGPT 7929 2026 02.12 내가 올해 몇살이더라? (내 나이 계산기) 6811 2026 02.11 AGI 자기 거버넌스 구조와 인간-AGI 관계 모델 (written by GEMINI & GPT) 8090 1 2026 01.30

    세상의모든계산기 님의 최근 댓글

    - claude AI는 l-c*r^2 을 1-c*r^2 으로 잘못 읽고 표시하고 있습니다. - TI-nspire CAS 계산기에 l-c*r^2 ≥0 을 조건에 추가해 계산해 보아도 결과는 바뀌지 않습니다. 2026 07.20 ⚠️ 경고가 바로 두 번째 방법이 "성공"한 이유와 정확히 연결되어 있습니다.   경고의 의미 "Domain of the result might be larger than the domain of the input"는 CAS가 절댓값(모듈러스)을 계산하는 과정에서 원래 식보다 정의역이 더 넓은 형태로 단순화했다는 뜻입니다. 구체적으로 이 계산은 내부적으로 대략 이런 과정을 거칩니다. $$\left|\frac{er}{e\cdot r}\right| = \sqrt{\left(\frac{er}{e\cdot r}\right)\cdot\overline{\left(\frac{er}{e\cdot r}\right)}}$$ 즉 원래 식(복소수)과 그 켤레복소수를 곱해서 실수부·허수부 제곱합을 만들고, 거기에 다시 제곱근을 씌우는 과정입니다. 이 과정에서 √(x²) → x 또는 √a·√b → √(ab) 같은 규칙들이 쓰이는데, 이런 규칙들은 x가 실수이고 0 이상일 때만 엄밀하게 성립합니다. CAS는 이 조건들을 일일이 다 추적하지 않고 넘어가면서, 원래는 (e≠0, r+l·ω·i ≠ 0 등) 복소수 특유의 좁은 정의역을 가진 식을, r, l, ω가 어떤 실수여도(부호 무관하게) 정의되는 1/√(r²+l²·ω²)라는 더 넓은 정의역의 식으로 바꿔버린 것입니다. CAS는 이 손실을 감지하고 경고를 띄운 것입니다. 이게 왜 조건 대입 성공과 연결되는가 정리하면, 이 경고는 사실상 이런 뜻입니다. "나는 이 결과를 만들면서 원래 식이 가지고 있던 정의역 제약 정보(부호 조건, i 관련 조건 등)를 이미 버렸다." 바로 이 "정의역 정보를 버린" 상태가 이후 con_1 대입을 매끄럽게 만드는 원인입니다. 첫 번째 시도에서는 i가 살아있는 원래 식에 조건을 대입했기 때문에, CAS가 √(1-c·r²)이 실수인지(정의역 조건: 1-c·r² ≥ 0) 계속 추적하려고 했고, 그 정보가 con에 없어서 더 이상 정리를 못 하고 멈췄습니다. 두 번째 시도에서는 절댓값 계산 단계에서 이미 그런 세밀한 정의역 추적을 CAS 스스로 포기(단순화)했기 때문에, 이후 ω에 무리식을 대입해도 "이게 실수가 맞나?" 하는 검증 절차 없이 그냥 대수적으로 치환·정리해 버립니다. 그래서 깔끔하게 √c/√l이 나온 겁니다. 한 줄 요약 절댓값 계산 시 뜬 "정의역이 넓어졌을 수 있다"는 경고는, CAS가 그 순간에 원래 식의 엄밀한 조건(정의역)을 놓쳤다는 신호이고, 바로 그 "조건을 놓친 상태"이기 때문에 뒤에 이어지는 조건식 대입이 막힘없이 진행된 것입니다. 다만 그 대가로, 결과인 1/√(r²+l²·ω²)이나 최종 √c/√l이 원래 회로 조건(i≠0이 되는 경계, 분모가 0이 되는 경우 등)에서는 엄밀히 성립하지 않을 수 있다는 점은 감안하셔야 합니다. 실제 물리적으로는 r, l, c > 0이고 결과도 물리적으로 타당한 형태라 문제없어 보이지만, 수학적 엄밀성 측면에서는 "정의역이 넓어진 근사적 결과"라는 꼬리표가 붙어있는 셈입니다. 2026 07.20 Claude AI 답변 TI-Nspire CAS의 | (such that / 조건대입) 연산자는 대입 시점의 수식 형태를 그대로 두고 기호만 치환하는 연산입니다. 대입 후에 처음부터 다시 "실수부/허수부 분리, 유리화" 같은 재간소화를 자동으로 수행하지 않습니다. 이 차이가 지금 보신 결과 차이의 핵심입니다. 첫 번째 경우 (|er/(e·r)| | con_1 and con → 실패) 이 시점의 식은 아직 r + l·ω·i 형태의 복소수 그대로입니다 (i가 살아있음). 여기에 con_1: ω = √(1-c·r²)/(√c·l)을 대입하면, 분모 안에 i · √(1-c·r²) 라는 항이 새로 생깁니다. 문제는 CAS가 √(1-c·r²)이 실수인지(즉 1-c·r² ≥ 0인지) 판단할 근거가 없다는 겁니다. 저장해 두신 con 조건은 c>0, l>0, r>0뿐이고, 1-c·r²≥0이라는 조건은 포함되어 있지 않습니다. 그래서 CAS는 i와 이 무리식을 더 정리(유리화, 실수부·허수부 재결합)하지 못하고 있는 그대로 남겨둡니다. 결과에 여전히 i가 보이는 이유입니다. 두 번째 경우 (결과값 1/√(r²+l²·ω²)에 대입 → 성공) |er/(e·r)| (절댓값/모듈러스) 연산은 그 자체로 이미 "복소수 → 실수" 변환을 완료한 결과입니다. 즉 1/√(r²+l²·ω²)에는 더 이상 i가 없고, 순수하게 r, l, ω로만 이루어진 실수식입니다. 이 식에 ω = con_1을 대입하는 것은 그냥 실수 대수식에 실수 대입하는 것이므로, i와 무리식이 뒤엉키는 문제 자체가 발생하지 않습니다. 그래서 바로 깔끔하게 √c/√l로 정리됩니다. 정리하면 절댓값(모듈러스) 계산은 "이 값들이 실수다"라는 가정을 이미 내부적으로 써서 i를 제거하는 연산입니다. 그 연산을 조건 대입 이전에 해두면, 이후 대입은 단순 실수 대입이라 문제없이 정리됩니다. 반대로 i가 남아있는 식에 무리식을 포함한 조건을 먼저 대입하면, 그 무리식의 실수성/부호에 대한 가정이 없어서 CAS가 i를 소거하는 재간소화를 못 하고 멈춰버립니다. 실용적 팁: 복소식에 조건을 대입해야 할 때는 가능하면 절댓값·실수화(유리화) 등을 먼저 끝내서 i를 없앤 뒤 조건을 대입하거나, 대입 후 결과에 다시 simplify/expand/combine 같은 명령을 한 번 더 걸어주면 (필요한 도메인 조건과 함께) 정리가 되는 경우가 많습니다. 2026 07.19 저도 어림잡아 추측할 뿐이지 정확한 이유를 알지는 못합니다.    질문하신 사진을 그대로 (Gemini 3.5 Flash / ChatGPT / Claude Sonnet 5) AI에 넣어 보니  claude AI 가 제일 합리적인 답변을 주어서 이를 붙여 넣습니다.  2026 07.19 아 그렇네요. 감사합니다. ^^ 2026 04.28
    글쓴이의 서명작성글 감추기 
    • 댓글 입력
    • 에디터 전환
    댓글 쓰기 에디터 사용하기 닫기
    • view_headline 목록
    • 14px
    • 목록
      view_headline
    × CLOSE
    전체 일반 389 질문 508 웃김 2 팁 & 정보 16 퀴즈 2 리뷰 11 퍼옴 & 링크 6 공지 1
    기본 (0) 제목 날짜 수정 조회 댓글 추천 비추
    분류 정렬 검색
    등록된 글이 없습니다.
    • 글쓰기
    • 세상의 모든 계산기 자유(질문) 게시판
    • 세상의모든계산기
    • 사업자등록번호 703-91-02181
    • 세모계 all rights reserved.