개요
회사에 파일에 저장된 text를 불러와 처리하는 프로그램이 있는데 테스트로 echo someword >> file
이런식으로 파일 하나를 만든뒤 이 파일을 읽어들였더니 에러가 발생하였다. (해당 프로그램은 공백과 개행에 민감한데 기존엔 이 처리가 되어있지 않았다.)
분명 파일 저장시 공백을 추가하거나 개행처리를 하지 않았는데 왜 이럴까 의문스러웠다.
찾아보니 원인은 Unix/Linux 계열 시스템에서 텍스트 에디터(vi, vim, nano 등)로 파일을 저장할 때나 echo
등 POSIX 호환 명령어들을 사용해서 저장할 때 문자열 끝에 개행이 자동으로 추가된다는 것이었다.
POSIX 표준과 개행문자
이는 POSIX 표준을 따르는 것으로, 텍스트 파일은 개행문자로 끝나야 한다는 규칙에 기인한다.
관련 내용은 POSIX 문서에서 아래와 같은 내용을 찾아볼 수 있다.
문서 링크
Although the standard input is required to be a text file, and therefore will always end with a newline
(unless it is an empty file)
위 내용을 보면 text 파일 저장시 항상 newline으로 끝난다고 한다.
여기서 주목할 점은 빈 파일은 예외라는 것이다. 이는 불필요한 저장 공간 낭비를 방지하기 위함이다.
그리고 또 하나 주목할만한 내용이 있다.
the processing of continuation lines when the −r option is
not used can result in the input not ending with a . This occurs if the last line of the
input file ends with backslash . It is for this reason that ''if any'' is used in ''The
terminating (if any) shall be removed from the input'' in the description. It is not a
relaxation of the requirement for standard input to be a text file.
이는 두 가지 특별한 경우를 설명한다
- backslash와 newline으로 끝나는 라인의 경우(
backslash <newline>
) 연속된 라인으로 처리 - -r 옵션이 없을 때 마지막 라인이 backslash newline으로 끝나는 경우, 입력이 newline 없이 끝날 수 있음
예시 1: 연속된 라인 처리
# oneline.txt 파일 내용
Hello \
World \
This is one line
# read로 읽을시
# "Hello World This is one line"으로 처리됨
이 방식은 이미 Dockerfile이나 쉘 스크립트 작성시 흔히 사용되고 있던 것이다.
예시 2: 마지막 라인의 특수한 처리
# lastline.txt 파일 내용
First line
Second line \
# read로 읽을시
# First line <- 정상 처리
# Second line <- newline 없이 끝남 (backslash와 newline이 제거됨)
여기서 -r
옵션은 read 명령어의 옵션으로, backslash를 이스케이프 문자로 처리하지 않고 일반 문자로 처리하게 한다.
운영체제별 줄바꿈 문자
다양한 운영체제는 서로 다른 줄바꿈 문자를 사용한다:
- Unix/Linux: LF (Line Feed,
\n
) - Windows: CRLF (Carriage Return + Line Feed,
\r\n
) - 구형 macOS: CR (Carriage Return,
\r
)
이러한 차이는 파일을 다른 운영체제로 이동할 때 문제를 일으킬 수도 있다.
왜 이렇게 처리하는가?
1. 라인 기반 처리의 일관성
- Unix/POSIX 시스템의 많은 텍스트 처리 도구들(grep, sed, awk 등)이 라인 단위로 동작
- 각 라인이 newline으로 끝나도록 강제함으로써, 이런 도구들이 일관되게 동작 가능
2. EOF(End of File) 처리의 명확성
- 파일의 마지막 라인에도 newline이 있어야 한다는 규칙으로 인해, 파일이 불완전하게 잘렸는지 여부를 쉽게 확인 가능
- newline이 없는 마지막 라인은 파일이 제대로 닫히지 않았거나 전송이 완료되지 않았음을 의미
- git diff 나 Fork와 같은 Git GUI 도구에서는 EOF가 없는 경우 "No newline at end of file" 경고를 표시
3. 스트림 처리의 용이성
- 모든 라인이 newline으로 끝나기 때문에, 파일을 스트림으로 처리할 때 라인의 시작과 끝을 명확하게 구분 가능
- 파이프라인을 통한 데이터 처리의 신뢰성 향상
- CI/CD 파이프라인에서 로그 처리나 파일 처리의 안정성 확보
EOF 처리가 되지 않았을 때의 문제점
1. 마지막 라인 처리의 불일치
# EOF 없는 파일
echo -n "First line
Second line
Last line" > no_eof.txt
# 다른 도구들의 처리 방식이 다를 수 있음
while read line; do
echo "Read: $line"
done < no_eof.txt
# 일부 구현에서는 마지막 "Last line"을 놓칠 수 있음
2. 파일 연결 시 문제
# EOF 없는 두 파일을 연결할 때
cat file1 file2 > combined
# file1의 마지막 라인과 file2의 첫 라인이 의도치 않게 하나로 합쳐질 수 있음
3. 텍스트 처리 도구들의 예상치 못한 동작
# wc -l로 라인 수를 세는 경우
wc -l no_eof.txt # 실제 라인 수보다 1 적게 나올 수 있음
해결 방안
1. 개발 도구 설정
Git 설정
# 줄바꿈 문자 자동 변환 설정
git config --global core.autocrlf true # Windows
git config --global core.autocrlf input # Unix/Linux/Mac
# 줄바꿈 문자 설정
git config --global core.eol lf
만약 팀원들이 다양한 OS를 사용하고 있다면, 개행처리를 하나의 방식으로(여기에선 LF) 통일 하기위한 설정이다.
2. 프로그램에서의 처리
코딩시 공백과 줄바꿈 처리를 하여 예상치 못한 동작을 방지.
3. 개행 없이 파일 생성
# printf 사용
printf '%s' "content" > input.txt
# echo 사용 (-n 옵션: no newline)
echo -n "content" > input.txt
결론
OS나 도구에 따라 예상치 못한 문제가 발생할 수 있으므로, 프로그램에서 공백/개행 처리를 명시적으로 해주는 것이 좋다. 또한 개발 도구의 적절한 설정을 통해 일관된 파일 포맷을 유지하는 것이 중요하다.
'IT > 이것저것' 카테고리의 다른 글
Cloudflare ICN 리전 사용에 관한 짧은 고찰 (0) | 2024.11.28 |
---|---|
[한글 특수기호] 사람마다 다른 한글 특수기호 깨짐 현상 (0) | 2024.11.10 |
[ChatGPT] GPTs 커스텀 플러그인 만들기 (0) | 2024.04.04 |
[문서 생성기] 문서 생성 오픈소스 알아보기 (1) | 2024.03.12 |
[유니코드 정규화] 같은 글자가 중복으로 저장되거나 검색에서 누락 된다면 유니코드를 의심해보자 (0) | 2024.02.21 |