다른 명령
import os
import re
from pathlib import Path
import argparse
import sys
from datetime import datetime
class TextFileSearcher:
def __init__(self):
# 기본 텍스트 파일 확장자
self.text_extensions = {'.txt', '.log', '.sql', '.py', '.java', '.js', '.html',
'.xml', '.json', '.csv', '.ini', '.cfg', '.conf',
'.properties', '.yml', '.yaml', '.md', '.bat', '.sh'}
def search_files(self, root_path, search_text, case_sensitive=False,
use_regex=False, file_extensions=None, encoding='utf-8'):
"""
지정된 폴더와 하위 디렉토리에서 텍스트 파일 내용을 검색
Args:
root_path: 검색할 루트 폴더 경로
search_text: 검색할 텍스트 또는 정규식
case_sensitive: 대소문자 구분 여부
use_regex: 정규식 사용 여부
file_extensions: 검색할 파일 확장자 (None이면 기본 확장자 사용)
encoding: 파일 인코딩
"""
results = []
root_path = Path(root_path)
if not root_path.exists():
print(f"경로가 존재하지 않습니다: {root_path}")
return results
# 검색할 파일 확장자 설정
extensions = file_extensions if file_extensions else self.text_extensions
# 정규식 패턴 컴파일
if use_regex:
try:
pattern = re.compile(search_text, 0 if case_sensitive else re.IGNORECASE)
except re.error as e:
print(f"정규식 오류: {e}")
return results
else:
if not case_sensitive:
search_text = search_text.lower()
print(f"검색 시작: {root_path}")
print(f"검색어: {search_text}")
print(f"정규식 사용: {use_regex}")
print(f"대소문자 구분: {case_sensitive}")
print("-" * 50)
file_count = 0
match_count = 0
# 모든 파일 재귀 탐색
for file_path in root_path.rglob('*'):
if file_path.is_file() and file_path.suffix.lower() in extensions:
file_count += 1
try:
# 파일 내용 읽기 (여러 인코딩 시도)
content = self._read_file_with_encoding(file_path, encoding)
if content is None:
continue
# 검색 수행
matches = self._search_in_content(content, search_text,
case_sensitive, use_regex, pattern if use_regex else None)
if matches:
match_count += 1
result = {
'file_path': str(file_path),
'relative_path': str(file_path.relative_to(root_path)),
'matches': matches,
'file_size': file_path.stat().st_size,
'modified_time': datetime.fromtimestamp(file_path.stat().st_mtime)
}
results.append(result)
# 실시간 결과 출력
print(f"✓ {result['relative_path']} ({len(matches)} 건)")
except Exception as e:
print(f"✗ 파일 읽기 오류 {file_path}: {e}")
print("-" * 50)
print(f"검색 완료: {file_count}개 파일 중 {match_count}개 파일에서 발견")
return results
def _read_file_with_encoding(self, file_path, primary_encoding='utf-8'):
"""다양한 인코딩으로 파일 읽기 시도"""
encodings = [primary_encoding, 'utf-8', 'cp949', 'euc-kr', 'latin1', 'ascii']
for encoding in encodings:
try:
with open(file_path, 'r', encoding=encoding, errors='ignore') as f:
return f.read()
except (UnicodeDecodeError, UnicodeError):
continue
except Exception:
return None
return None
def _search_in_content(self, content, search_text, case_sensitive, use_regex, pattern):
"""파일 내용에서 텍스트 검색"""
matches = []
if use_regex:
# 정규식 검색
for match in pattern.finditer(content):
line_num = content[:match.start()].count('\n') + 1
line_content = content.split('\n')[line_num - 1].strip()
matches.append({
'line_number': line_num,
'line_content': line_content,
'match_text': match.group(),
'start_pos': match.start(),
'end_pos': match.end()
})
else:
# 일반 텍스트 검색
lines = content.split('\n')
for i, line in enumerate(lines):
search_line = line if case_sensitive else line.lower()
if search_text in search_line:
matches.append({
'line_number': i + 1,
'line_content': line.strip(),
'match_text': search_text
})
return matches
def print_detailed_results(self, results):
"""상세 검색 결과 출력"""
if not results:
print("검색 결과가 없습니다.")
return
print(f"\n=== 상세 검색 결과 ({len(results)}개 파일) ===")
for i, result in enumerate(results, 1):
print(f"\n[{i}] {result['relative_path']}")
print(f" 크기: {result['file_size']:,} bytes")
print(f" 수정일: {result['modified_time'].strftime('%Y-%m-%d %H:%M:%S')}")
print(f" 매치: {len(result['matches'])}건")
for match in result['matches'][:5]: # 최대 5개만 표시
print(f" 라인 {match['line_number']}: {match['line_content'][:100]}")
if len(result['matches']) > 5:
print(f" ... 외 {len(result['matches']) - 5}건 더")
def save_results(self, results, output_file):
"""검색 결과를 파일로 저장"""
try:
with open(output_file, 'w', encoding='utf-8') as f:
f.write(f"텍스트 파일 검색 결과\n")
f.write(f"생성일시: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"총 {len(results)}개 파일에서 발견\n")
f.write("=" * 80 + "\n\n")
for i, result in enumerate(results, 1):
f.write(f"[{i}] {result['file_path']}\n")
f.write(f"크기: {result['file_size']:,} bytes, ")
f.write(f"수정일: {result['modified_time'].strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"매치 건수: {len(result['matches'])}\n")
for match in result['matches']:
f.write(f" 라인 {match['line_number']}: {match['line_content']}\n")
f.write("\n" + "-" * 80 + "\n\n")
print(f"결과가 저장되었습니다: {output_file}")
except Exception as e:
print(f"파일 저장 오류: {e}")
def main():
parser = argparse.ArgumentParser(description='폴더와 하위 디렉토리의 텍스트 파일에서 내용 검색')
parser.add_argument('path', help='검색할 폴더 경로')
parser.add_argument('search_text', help='검색할 텍스트')
parser.add_argument('-i', '--ignore-case', action='store_true', help='대소문자 무시')
parser.add_argument('-r', '--regex', action='store_true', help='정규식 사용')
parser.add_argument('-e', '--extensions', help='파일 확장자 (쉼표로 구분, 예: .txt,.log,.sql)')
parser.add_argument('-o', '--output', help='결과 저장할 파일명')
parser.add_argument('--encoding', default='utf-8', help='파일 인코딩 (기본값: utf-8)')
parser.add_argument('-d', '--detail', action='store_true', help='상세 결과 출력')
args = parser.parse_args()
# 파일 확장자 파싱
extensions = None
if args.extensions:
extensions = set(ext.strip() for ext in args.extensions.split(','))
# 검색 실행
searcher = TextFileSearcher()
results = searcher.search_files(
root_path=args.path,
search_text=args.search_text,
case_sensitive=not args.ignore_case,
use_regex=args.regex,
file_extensions=extensions,
encoding=args.encoding
)
# 결과 출력
if args.detail:
searcher.print_detailed_results(results)
# 결과 저장
if args.output:
searcher.save_results(results, args.output)
# 직접 실행할 때 사용하는 간단한 인터페이스
def simple_search():
"""대화형 검색 인터페이스"""
print("=== 텍스트 파일 검색 프로그램 ===")
folder_path = input("검색할 폴더 경로: ").strip()
search_text = input("검색할 텍스트: ").strip()
use_regex = input("정규식 사용하시겠습니까? (y/n): ").strip().lower() == 'y'
case_sensitive = input("대소문자를 구분하시겠습니까? (y/n): ").strip().lower() == 'y'
searcher = TextFileSearcher()
results = searcher.search_files(folder_path, search_text, case_sensitive, use_regex)
if results:
show_detail = input("\n상세 결과를 보시겠습니까? (y/n): ").strip().lower() == 'y'
if show_detail:
searcher.print_detailed_results(results)
save_file = input("결과를 파일로 저장하시겠습니까? (파일명 입력 또는 Enter): ").strip()
if save_file:
searcher.save_results(results, save_file)
if __name__ == "__main__":
# 명령행 인수가 있으면 CLI 모드, 없으면 대화형 모드
if len(sys.argv) > 1:
main()
else:
simple_search()