본문으로 건너뛰기

Shell 스크립트 'No such file or directory' 오류 완벽 해결법

· 약 5분
Jeongyong Park
쌍팔년생 개발자

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\n0D 0ACarriage Return + Line Feed
Unix/Linux\n0ALine Feed만 사용
Mac (구버전)\r0DCarriage Return만 사용
Mac (현재)\n0AUnix와 동일

왜 오류가 발생하는가?

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 환경에서 테스트해보세요

참고 자료

📢 AdSense 광고 영역로딩 중...

💬 댓글 시스템