텍스트를 다루다 보면 대소문자 변환, 특정 문자 삭제, 공백 정리 등의 작업을 자주 하게 됩니다. tr(translate)은 이러한 문자 단위 변환과 삭제 작업을 빠르고 간단하게 처리하는 리눅스 명령어입니다. 이번 포스트에서는 tr의 기초부터 실무 활용까지 상세히 알아보겠습니다.
tr란 무엇인가?
tr은 표준 입력에서 문자를 읽어 변환하거나 삭제한 후 표준 출력으로 내보내는 명령어입니다. 파이프라인에서 주로 사용되며, 간단하면서도 강력한 텍스트 처리 기능을 제공합니다.
tr의 주요 특징
- 문자 변환: 특정 문자를 다른 문자로 치환
- 문자 삭제: 지정한 문자 제거
- 중복 제거: 연속된 동일 문자를 하나로 압축
- 문자 클래스: 미리 정의된 문자 집합 사용
- 스트림 처리: 파이프와 함께 사용하기 최적화
기본 특성
# tr은 파일을 직접 읽지 않음
tr 'a' 'b' file.txt # 작동하지 않음
# 표준 입력으로 받아야 함
cat file.txt | tr 'a' 'b' # 올바른 사용
tr 'a' 'b' < file.txt # 리다이렉션 사용
기본 문법
기본 형식
tr [옵션] SET1 [SET2]
주요 옵션
-d, --delete # SET1의 문자 삭제
-s, --squeeze-repeats # 연속된 문자를 하나로 압축
-c, --complement # SET1의 보수(반대) 집합 사용
-t, --truncate-set1 # SET1을 SET2 길이에 맞춤
기본 사용법
1. 문자 변환 (Translate)
단일 문자 변환
# 소문자 a를 대문자 A로 변환
echo "apple" | tr 'a' 'A'
# 출력: Apple
# 쉼표를 공백으로 변환
echo "apple,banana,cherry" | tr ',' ' '
# 출력: apple banana cherry
# 하이픈을 밑줄로 변환
echo "hello-world" | tr '-' '_'
# 출력: hello_world
여러 문자 동시 변환
# 여러 문자를 일대일 매핑
echo "hello" | tr 'helo' 'HELO'
# 출력: HELLo
# 숫자를 별표로 변환
echo "abc123xyz" | tr '0-9' '*'
# 출력: abc***xyz
# 모음 변환
echo "hello world" | tr 'aeiou' 'AEIOU'
# 출력: hEllO wOrld
2. 범위를 사용한 변환
문자 범위
# 소문자를 대문자로 (a-z → A-Z)
echo "hello world" | tr 'a-z' 'A-Z'
# 출력: HELLO WORLD
# 대문자를 소문자로 (A-Z → a-z)
echo "HELLO WORLD" | tr 'A-Z' 'a-z'
# 출력: hello world
# 특정 범위 변환
echo "abc123xyz" | tr 'a-z' 'A-Z'
# 출력: ABC123XYZ
숫자 범위
# 숫자를 문자로
echo "123" | tr '0-9' 'abcdefghij'
# 출력: bcd
# 숫자를 X로 마스킹
echo "Card: 1234-5678-9012" | tr '0-9' 'X'
# 출력: Card: XXXX-XXXX-XXXX
3. 문자 삭제 (-d 옵션)
# 특정 문자 삭제
echo "hello world" | tr -d 'o'
# 출력: hell wrld
# 숫자 삭제
echo "abc123def456" | tr -d '0-9'
# 출력: abcdef
# 공백 삭제
echo "h e l l o" | tr -d ' '
# 출력: hello
# 여러 문자 삭제
echo "hello@world#123" | tr -d '@#0-9'
# 출력: helloworld
# 개행 문자 삭제
cat file.txt | tr -d '\n'
4. 중복 문자 압축 (-s 옵션)
# 연속된 공백을 하나로
echo "hello world" | tr -s ' '
# 출력: hello world
# 연속된 개행을 하나로
cat file.txt | tr -s '\n'
# 연속된 동일 문자 제거
echo "hellooo wooorld" | tr -s 'o'
# 출력: hello world
# 모든 연속 공백문자를 하나로
echo "hello\t\t\tworld" | tr -s '[:space:]'
# 출력: hello world
5. 보수 집합 사용 (-c 옵션)
# SET1에 없는 모든 문자 삭제
echo "hello123world" | tr -cd '0-9'
# 출력: 123
# 영문자와 숫자만 남기기
echo "hello@123#world!" | tr -cd 'a-zA-Z0-9'
# 출력: hello123world
# 공백 제외 모두 삭제
echo "keep these spaces" | tr -cd '[:space:]'
# 출력: (공백만 남음)
# 알파벳만 남기고 모두 삭제
echo "Test-123@email.com" | tr -cd 'a-zA-Z'
# 출력: Testemailcom
문자 클래스 (Character Classes)
tr은 POSIX 문자 클래스를 지원하여 더 편리한 문자 집합 지정이 가능합니다.
주요 문자 클래스
[:alnum:] # 알파벳과 숫자 (a-zA-Z0-9)
[:alpha:] # 알파벳 (a-zA-Z)
[:digit:] # 숫자 (0-9)
[:lower:] # 소문자 (a-z)
[:upper:] # 대문자 (A-Z)
[:space:] # 공백 문자 (space, tab, newline 등)
[:blank:] # 공백과 탭
[:punct:] # 구두점
[:print:] # 출력 가능한 문자
[:cntrl:] # 제어 문자
문자 클래스 사용 예제
# 소문자를 대문자로
echo "hello world" | tr '[:lower:]' '[:upper:]'
# 출력: HELLO WORLD
# 숫자 삭제
echo "abc123xyz" | tr -d '[:digit:]'
# 출력: abcxyz
# 구두점 삭제
echo "hello, world!" | tr -d '[:punct:]'
# 출력: hello world
# 공백 문자를 개행으로
echo "hello world test" | tr '[:space:]' '\n'
# 출력:
# hello
# world
# test
# 알파벳이 아닌 것을 삭제
echo "hello123world" | tr -cd '[:alpha:]'
# 출력: helloworld
실전 활용 예제
1. 대소문자 변환
# 파일 내용 전체를 대문자로
cat file.txt | tr '[:lower:]' '[:upper:]'
# 파일 내용 전체를 소문자로
cat file.txt | tr '[:upper:]' '[:lower:]'
# 파이프라인에서 사용
ls | tr '[:lower:]' '[:upper:]'
# 변수 내용 변환
TEXT="Hello World"
echo "$TEXT" | tr '[:upper:]' '[:lower:]'
# 출력: hello world
2. 공백 처리
# 여러 공백을 하나로
echo "hello world test" | tr -s ' '
# 출력: hello world test
# 탭을 공백으로
cat file.txt | tr '\t' ' '
# 공백을 밑줄로
echo "hello world test" | tr ' ' '_'
# 출력: hello_world_test
# 모든 공백을 제거
echo "h e l l o" | tr -d ' '
# 출력: hello
# 줄 끝 공백 제거 (다른 도구와 조합)
cat file.txt | sed 's/[[:space:]]*$//' | tr -s ' '
3. 파일 포맷 변환
Windows → Unix 줄바꿈 변환
# CRLF를 LF로 (Windows → Unix)
cat windows.txt | tr -d '\r' > unix.txt
# 확인
file unix.txt
CSV 처리
# 쉼표를 탭으로
cat data.csv | tr ',' '\t' > data.tsv
# 쉼표를 파이프로
cat data.csv | tr ',' '|'
# 쉼표를 개행으로 (각 필드를 한 줄씩)
echo "apple,banana,cherry" | tr ',' '\n'
# 출력:
# apple
# banana
# cherry
4. 데이터 정제
전화번호 포맷팅
# 하이픈 제거
echo "010-1234-5678" | tr -d '-'
# 출력: 01012345678
# 숫자만 추출
echo "Tel: 010-1234-5678" | tr -cd '0-9'
# 출력: 01012345678
이메일 주소 정제
# 이메일에서 특수문자 제거
echo "user@example.com" | tr -d '@.'
# 출력: userexamplecom
# 이메일 도메인만 추출 (다른 명령어와 조합)
echo "user@example.com" | cut -d@ -f2
URL 인코딩 준비
# 공백을 + 또는 %20으로
echo "hello world" | tr ' ' '+'
# 출력: hello+world
# 특수문자 제거
echo "hello world!" | tr -cd 'a-zA-Z0-9 '
# 출력: hello world
5. 로그 파일 처리
# IP 주소에서 점 제거
echo "192.168.1.1" | tr -d '.'
# 출력: 192168011
# 로그에서 특정 문자 마스킹
cat access.log | tr '0-9' 'X'
# 로그 파일 병합 (개행을 공백으로)
cat log.txt | tr '\n' ' '
# 여러 줄을 한 줄로
cat multiline.txt | tr -s '\n' ' '
6. 비밀번호 및 보안
# 숫자 마스킹
echo "Password: 1234" | tr '0-9' '*'
# 출력: Password: ****
# 신용카드 번호 마스킹
echo "1234-5678-9012-3456" | tr '0-9' 'X'
# 출력: XXXX-XXXX-XXXX-XXXX
# 특정 문자만 남기고 마스킹
echo "ID: user123" | tr -c 'a-zA-Z: \n' '*'
# 출력: ID: user***
7. 텍스트 분석 준비
# 단어별로 나누기 (공백을 개행으로)
echo "hello world test" | tr ' ' '\n'
# 문장 부호 제거
cat essay.txt | tr -d '[:punct:]'
# 소문자 변환 후 정렬
cat words.txt | tr '[:upper:]' '[:lower:]' | sort
# 단어 빈도 분석 준비
cat text.txt | tr -cs '[:alpha:]' '\n' | sort | uniq -c | sort -rn
8. 스크립트에서 활용
#!/bin/bash
# 사용자 입력을 소문자로 변환
read -p "Enter filename: " filename
filename_lower=$(echo "$filename" | tr '[:upper:]' '[:lower:]')
echo "Lowercase: $filename_lower"
# 파일명에서 공백을 밑줄로
for file in *; do
newname=$(echo "$file" | tr ' ' '_')
if [ "$file" != "$newname" ]; then
mv "$file" "$newname"
fi
done
# 비밀번호 강도 체크 (숫자 개수)
password="Pass123word"
digit_count=$(echo "$password" | tr -cd '[:digit:]' | wc -c)
echo "숫자 개수: $digit_count"
고급 활용 패턴
1. 옵션 조합
# 중복 제거와 삭제 동시 사용
echo "hello world" | tr -ds ' ' ''
# 연속 공백을 하나로 만들고 삭제
# 보수와 삭제
echo "keep123only456numbers" | tr -cd '[:digit:]'
# 출력: 123456
# 보수와 압축
echo "hello123world456" | tr -cs '[:alpha:]' '\n'
# 알파벳이 아닌 것을 개행으로, 연속된 것은 하나로
# 여러 옵션 조합
echo "HELLO WORLD" | tr -s '[:upper:]' '[:lower:]'
# 대문자를 소문자로 변환하고 연속 문자 압축
2. ROT13 암호화
# ROT13 인코딩
echo "hello" | tr 'a-zA-Z' 'n-za-mN-ZA-M'
# 출력: uryyb
# ROT13 디코딩 (같은 명령어로 가능)
echo "uryyb" | tr 'a-zA-Z' 'n-za-mN-ZA-M'
# 출력: hello
# 파일 전체 ROT13
cat secret.txt | tr 'a-zA-Z' 'n-za-mN-ZA-M' > encoded.txt
3. 문자 매핑 테이블
# 자음을 별표로
echo "hello world" | tr 'bcdfghjklmnpqrstvwxyz' '*'
# 출력: *e**o *o***
# 숫자를 한글로 (유니코드)
echo "123" | tr '123' '일이삼'
# 키보드 위치 변환 (QWERTY → Dvorak)
echo "hello" | tr 'qwertyuiop' 'pyfgcrlao'
4. 파이프라인 최적화
# 여러 tr 명령을 하나로
# 비효율적
cat file.txt | tr 'a-z' 'A-Z' | tr -d '[:punct:]' | tr -s ' '
# 효율적
cat file.txt | tr 'a-z' 'A-Z' | tr -d '[:punct:]' -s ' '
# 더 효율적 (하지만 복잡도 증가)
cat file.txt | tr 'a-z[:punct:]' 'A-Z' | tr -s ' '
tr vs 다른 도구 비교
tr vs sed
# tr: 단순 문자 변환
echo "hello" | tr 'a-z' 'A-Z'
# sed: 패턴 기반 치환
echo "hello" | sed 's/hello/HELLO/'
# tr이 더 빠름 (대용량 파일)
cat large.txt | tr 'a-z' 'A-Z' # 빠름
cat large.txt | sed 's/./\U&/g' # 느림
tr vs awk
# tr: 문자 단위 처리
echo "hello world" | tr ' ' '\n'
# awk: 필드 단위 처리
echo "hello world" | awk '{print $1; print $2}'
# 각각의 장점 활용
cat data.txt | tr ',' ' ' | awk '{print $1}'
tr vs perl
# tr: 간단한 변환
echo "hello" | tr 'a-z' 'A-Z'
# perl: 복잡한 패턴
echo "hello" | perl -pe 's/(\w+)/\U$1/'
특수 문자 처리
이스케이프 시퀀스
# 탭 문자
echo "hello\tworld" | tr '\t' ' '
# 개행 문자
echo -e "hello\nworld" | tr '\n' ' '
# 캐리지 리턴
cat file.txt | tr -d '\r'
# 널 문자
cat file.txt | tr '\0' '\n'
# 백슬래시
echo "path\\to\\file" | tr '\\' '/'
8진수 표기
# ASCII 코드로 지정
echo "hello" | tr '\141-\172' '\101-\132' # a-z → A-Z
# 특수 문자
echo "test" | tr 't' '\011' # t를 탭으로
성능 최적화
대용량 파일 처리
# 버퍼링 사용
cat large_file.txt | tr 'a-z' 'A-Z' > output.txt
# 병렬 처리 (GNU parallel)
cat large_file.txt | parallel --pipe tr 'a-z' 'A-Z' > output.txt
# 메모리 효율적 처리
tr 'a-z' 'A-Z' < large_file.txt > output.txt
불필요한 파이프 제거
# 비효율적
cat file.txt | tr 'a-z' 'A-Z'
# 효율적 (cat 불필요)
tr 'a-z' 'A-Z' < file.txt
# 가장 효율적 (리다이렉션)
tr 'a-z' 'A-Z' < input.txt > output.txt
일반적인 실수와 해결책
1. 파일명을 직접 지정
# 오류: tr은 파일을 직접 읽지 않음
tr 'a-z' 'A-Z' file.txt # 작동 안 함
# 해결책 1: 리다이렉션
tr 'a-z' 'A-Z' < file.txt
# 해결책 2: cat 사용
cat file.txt | tr 'a-z' 'A-Z'
2. SET1과 SET2 길이 불일치
# 문제: SET2가 더 짧음
echo "abc" | tr 'abc' 'XY'
# 출력: XYY (c도 Y로 변환됨)
# 해결책: -t 옵션으로 SET1 자르기
echo "abc" | tr -t 'abc' 'XY'
# 출력: XYc
# 또는 SET2를 맞춤
echo "abc" | tr 'abc' 'XYZ'
# 출력: XYZ
3. 특수문자 이스케이프
# 문제: 특수문자 처리 안 됨
echo "hello world" | tr ' ' '\t' # 작동 안 함 (셸에서 해석)
# 해결책: 따옴표 사용
echo "hello world" | tr ' ' '\t' # 작동
# 또는 $를 사용
echo "hello world" | tr ' ' $'\t'
4. 범위 지정 오류
# 문제: 범위가 역순
echo "abc" | tr 'z-a' 'A-Z' # 오류 발생
# 해결책: 올바른 순서
echo "abc" | tr 'a-z' 'A-Z'
5. 유니코드 문자
# 문제: 한글 등 멀티바이트 문자는 처리 안 됨
echo "안녕하세요" | tr 'ㄱ-ㅎ' 'a-z' # 작동 안 함
# 해결책: 다른 도구 사용
echo "안녕하세요" | sed 's/안녕/hello/'
# ASCII만 처리할 때는 LC_ALL 설정
LC_ALL=C tr ...
디버깅 팁
변환 결과 확인
# 중간 결과 확인
echo "test data" | tr 'a-z' 'A-Z' | cat -v
# 16진수로 확인
echo "test" | tr 'a-z' 'A-Z' | od -x
# 각 단계 확인
echo "test data" | tee >(cat > /dev/stderr) | tr 'a-z' 'A-Z'
문자 집합 확인
# SET1에 포함된 문자 확인
printf '%s\n' {a..z} | tr 'aeiou' 'X'
# 문자 클래스 내용 확인
printf '%s' {a..z} | tr '[:lower:]' '\n'
유용한 tr 원라이너
# 줄 번호 제거 (숫자와 점 삭제)
cat -n file.txt | tr -d '0-9.'
# 파일 확장자 소문자로
for f in *; do mv "$f" "$(echo $f | tr '[:upper:]' '[:lower:]')"; done
# 단어를 한 줄에 하나씩
cat text.txt | tr -cs '[:alpha:]' '\n'
# 파일 크기 읽기 쉽게 (3자리마다 쉼표)
ls -l | awk '{print $5}' | tr '\n' ',' | sed 's/,$/\n/'
# 이메일 주소 도메인만 추출
echo "user@example.com" | tr '@' '\n' | tail -1
# 문자열 역순 (rev 대체)
echo "hello" | tr 'a-z' 'z-a' # 이건 작동 안 함
echo "hello" | rev # rev 사용 권장
# 중복 문자 개수 세기
echo "hello" | tr -cd 'l' | wc -c
# 알파벳 빈도 분석
cat text.txt | tr -cd '[:alpha:]' | fold -w1 | sort | uniq -c | sort -rn
# 모든 공백을 하나의 공백으로
cat text.txt | tr -s '[:space:]' ' '
# ROT13 암호화/복호화
alias rot13="tr 'a-zA-Z' 'n-za-mN-ZA-M'"
echo "secret" | rot13
실무 스크립트 예제
로그 분석 스크립트
#!/bin/bash
# 로그 파일에서 IP 추출 및 정리
LOG_FILE="access.log"
# IP 주소만 추출하고 정렬
cat $LOG_FILE | \
tr -s ' ' | \
cut -d' ' -f1 | \
sort | \
uniq -c | \
sort -rn
데이터 정제 스크립트
#!/bin/bash
# CSV 데이터 정제
INPUT="data.csv"
OUTPUT="cleaned.csv"
# 대문자로 변환, 연속 공백 제거, 특수문자 삭제
cat $INPUT | \
tr '[:lower:]' '[:upper:]' | \
tr -s ' ' | \
tr -d '[:cntrl:]' > $OUTPUT
echo "데이터 정제 완료: $OUTPUT"
파일명 일괄 변경
#!/bin/bash
# 파일명의 공백을 밑줄로 변경
for file in *; do
# 공백을 밑줄로 변환
newname=$(echo "$file" | tr ' ' '_')
# 대문자를 소문자로
newname=$(echo "$newname" | tr '[:upper:]' '[:lower:]')
# 파일명이 변경되었으면 rename
if [ "$file" != "$newname" ]; then
echo "Renaming: $file -> $newname"
mv "$file" "$newname"
fi
done
비밀번호 검증
#!/bin/bash
# 비밀번호 강도 체크
check_password() {
local password="$1"
# 길이 체크
length=${#password}
# 숫자 개수
digit_count=$(echo "$password" | tr -cd '[:digit:]' | wc -c)
# 대문자 개수
upper_count=$(echo "$password" | tr -cd '[:upper:]' | wc -c)
# 소문자 개수
lower_count=$(echo "$password" | tr -cd '[:lower:]' | wc -c)
# 특수문자 개수
special_count=$(echo "$password" | tr -cd '[:punct:]' | wc -c)
echo "비밀번호 분석:"
echo "- 길이: $length"
echo "- 숫자: $digit_count"
echo "- 대문자: $upper_count"
echo "- 소문자: $lower_count"
echo "- 특수문자: $special_count"
# 강도 판정
if [ $length -ge 12 ] && [ $digit_count -ge 1 ] && \
[ $upper_count -ge 1 ] && [ $lower_count -ge 1 ] && \
[ $special_count -ge 1 ]; then
echo "강도: 강함"
elif [ $length -ge 8 ]; then
echo "강도: 보통"
else
echo "강도: 약함"
fi
}
read -sp "비밀번호 입력: " password
echo
check_password "$password"
마무리
tr은 간단하지만 매우 유용한 텍스트 처리 도구입니다. 문자 단위의 변환과 삭제 작업에 특화되어 있으며, 파이프라인에서 다른 명령어와 함께 사용할 때 그 진가를 발휘합니다.
tr 마스터를 위한 학습 로드맵:
- 기본 변환과 삭제 익히기
- 범위와 문자 클래스 활용
- 옵션 조합 (-d, -s, -c) 마스터
- 파이프라인에서 다른 도구와 조합
- 실전 스크립트 작성 연습
핵심 팁:
- 간단할 때: tr은 sed나 awk보다 빠르고 간단
- 문자 단위: 한 글자씩 처리하는 작업에 최적화
- 파이프 활용: 다른 명령어와 조합하여 강력한 처리
- 성능: 대용량 파일도 빠르게 처리
- 제한 인식: 멀티바이트 문자는 처리 불가
tr은 작지만 강력한 도구입니다. 텍스트 처리 도구상자에서 가장 기본적이면서도 실용적인 도구 중 하나이니, 다양한 상황에서 적극 활용해보세요!