Shell 스크립트 'No such file or directory' 오류 완벽 해결법
· 약 5분
Windows에서 작성한 Shell 스크립트를 Linux나 WSL에서 실행할 때 발생하는 'No such file or directory' 오류의 원인과 해결방법을 알아보겠습니다.
이 글에서 다룰 내용:
- DOS/Windows와 Unix/Linux 줄바꿈 차이점 이해
- 다양한 해결방법과 각각의 장단점
- 문제 예방을 위한 개발 환경 설정
- 실무에서 유용한 팁과 도구들
TL;DR: Windows에서 작성한 스크립트의 CRLF 줄바꿈이 Linux에서 문제를 일으킵니다.
dos2unix
명령어나sed 's/\r$//'
로 해결하고, Git 설정과 .gitattributes로 예방하세요.
문제 상황
Linux, WSL, Git Bash 등에서 Windows에서 작성한 스크립트를 실행할 때 다음과 같은 오류가 발생합니다.
에러 메시지
line 1: #!/bin/sh: No such file or directory
또는
line 1: #!/bin/bash: No such file or directory
문제 진단 방법
파일의 줄바꿈 형식을 확인하려면 다음 명령어를 사용하세요:
# 파일의 줄바꿈 문자를 시각적으로 확인
cat -v script.sh
# 16진수로 파일 내용 확인
hexdump -C script.sh | head
# od 명령어로 8진수 확인
od -bc script.sh
Windows 스타일 줄바꿈이 있다면 ^M
문자나 \r\n
시퀀스를 볼 수 있습니다.
원인 분석
운영체제별 줄바꿈 방식
운영체제 | 줄바꿈 문자 | 16진수 표현 | 설명 |
---|---|---|---|
Windows/DOS | \r\n | 0D 0A | Carriage Return + Line Feed |
Unix/Linux | \n | 0A | Line Feed만 사용 |
Mac (구버전) | \r | 0D | Carriage Return만 사용 |
Mac (현재) | \n | 0A | Unix와 동일 |
왜 오류가 발생하는가?
Linux 쉘에서 Windows 스타일의 줄바꿈이 포함된 스크립트를 실행하면, shebang(#!/bin/bash
) 뒤에 보이지 않는 \r
문자가 붙어서 /bin/bash\r
라는 존재하지 않는 경로를 찾게 됩니다.
주의사항: 이 문제는 스크립트 파일뿐만 아니라 설정 파일, 데이터 파일에서도 발생할 수 있습니다. 특히 CSV 파일이나 설정 파일을 처리할 때 예상치 못한 동작을 일으킬 수 있습니다.
해결방법
1. dos2unix 명령어 사용 (권장)
가장 간단하고 안전한 방법입니다.
# dos2unix 설치
# Ubuntu/Debian
sudo apt-get install dos2unix
# CentOS/RHEL/Fedora
sudo yum install dos2unix
# 또는 최신 버전에서
sudo dnf install dos2unix
# 파일 변환
dos2unix script.sh
# 원본 파일 백업하면서 변환
dos2unix -b script.sh
# 여러 파일 한번에 변환
dos2unix *.sh
# 재귀적으로 모든 .sh 파일 변환
find . -name "*.sh" -type f -exec dos2unix {} \;
2. sed 명령어 사용
# 기본 사용법
sed -i 's/\r$//' script.sh
# 원본 파일 백업하면서 변환
sed -i.bak 's/\r$//' script.sh
# 새 파일로 출력
sed 's/\r$//' script.sh > script_fixed.sh
# 여러 파일 처리
sed -i 's/\r$//' *.sh
3. tr 명령어 사용
# 캐리지 리턴 문자 제거
tr -d '\r' < script.sh > script_fixed.sh
# 또는
cat script.sh | tr -d '\r' > script_fixed.sh
# 8진수 표현 사용
tr -d '\015' < script.sh > script_fixed.sh
4. vim/vi 에디터에서 직접 변환
# vim으로 파일 열기
vim script.sh
# 명령 모드에서 다음 명령 실행
:set ff=unix
:wq
# 또는 한 번에
:set fileformat=unix | wq
5. awk 명령어 사용
# awk로 줄바꿈 변환
awk '{ sub(/\r$/, ""); print }' script.sh > script_fixed.sh
# 또는
awk '{ gsub(/\r/, ""); print }' script.sh > script_fixed.sh
6. Perl 원라이너 사용
# Perl로 in-place 변환
perl -pi -e 's/\r\n/\n/g' script.sh
# 백업 파일 생성하면서 변환
perl -pi.bak -e 's/\r\n/\n/g' script.sh
예방 방법
1. Git 설정으로 자동 변환
# Windows에서: 체크아웃 시 CRLF로, 커밋 시 LF로 자동 변환
git config --global core.autocrlf true
# Linux/Mac에서: 커밋 시 CRLF를 LF로 변환, 체크아웃 시 변환 안함
git config --global core.autocrlf input
# 모든 플랫폼에서 LF만 사용 (권장)
git config --global core.autocrlf false
git config --global core.eol lf
2. .gitattributes 파일 설정
프로젝트 루트에 .gitattributes
파일을 생성하여 파일별로 줄바꿈 설정:
# 모든 텍스트 파일은 LF 사용
* text=auto eol=lf
# 스크립트 파일은 반드시 LF 사용
*.sh text eol=lf
*.bash text eol=lf
# Windows 배치 파일은 CRLF 사용
*.bat text eol=crlf
*.cmd text eol=crlf
# 바이너리 파일은 변환하지 않음
*.png binary
*.jpg binary
*.exe binary
3. 에디터 설정
VS Code
{
"files.eol":"\n",
"files.insertFinalNewline":true,
"files.trimFinalNewlines":true
}
Sublime Text
- View → Line Endings → Unix
Notepad++
- 편집 → EOL 변경 → Unix (LF)
Atom
- Packages → Line Ending Selector → LF
4. EditorConfig 사용
프로젝트 루트에 .editorconfig
파일 생성:
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{sh,bash}]
end_of_line = lf
고급 활용법
대량 파일 처리
# 디렉토리 전체의 스크립트 파일 변환
find /path/to/scripts -name "*.sh" -type f -print0 | xargs -0 dos2unix
# 특정 확장자 파일들 일괄 변환
find . -type f \( -name "*.sh" -o -name "*.bash" -o -name "*.py" \) -exec dos2unix {} +
# 변환 전 백업 생성
find . -name "*.sh" -type f -exec cp {} {}.bak \; -exec dos2unix {} \;
스크립트로 자동화
#!/bin/bash
# fix_line_endings.sh
# 변환할 파일 확장자 목록
EXTENSIONS=("sh" "bash" "py" "pl" "rb")
for ext in "${EXTENSIONS[@]}"; do
echo "Converting *.${ext} files..."
find . -name "*.${ext}" -type f -exec dos2unix {} \;
done
echo "Line ending conversion completed!"
파일 형식 확인 스크립트
#!/bin/bash
# check_line_endings.sh
check_file() {
local file="$1"
if file "$file" | grep -q "CRLF"; then
echo "DOS/Windows: $file"
elif file "$file" | grep -q "CR"; then
echo "Mac (old): $file"
else
echo "Unix/Linux: $file"
fi
}
if [ $# -eq 0 ]; then
echo "Usage: $0 <file1> [file2] ..."
exit 1
fi
for file in "$@"; do
if [ -f "$file" ]; then
check_file "$file"
else
echo "File not found: $file"
fi
done
문제 해결 팁
1. 권한 문제 해결
# 파일 권한 확인
ls -la script.sh
# 실행 권한 추가
chmod +x script.sh
# 소유자 변경 (필요한 경우)
sudo chown $USER:$USER script.sh
2. 숨겨진 문자 확인
# 모든 비출력 문자 표시
cat -A script.sh
# 줄 번호와 함께 표시
cat -n script.sh | head -5
3. 대용량 파일 처리
# 큰 파일의 경우 메모리 효율적인 방법
sed 's/\r$//' large_script.sh > temp_file && mv temp_file large_script.sh
실무 팁
- 팀 프로젝트에서는
.gitattributes
와.editorconfig
파일을 반드시 설정하세요 - CI/CD 파이프라인에서 스크립트 실행 전 자동으로 줄바꿈을 확인하는 단계를 추가하는 것이 좋습니다
- Windows에서 개발할 때는 WSL을 사용하여 Linux 환경에서 테스트해보세요