빌드노트
Bash에서 쓰기 가능한 파일에 >가 거절된다
Bash의 noclobber가 켜지면 쓰기 가능한 기존 일반 파일도 >로 덮어쓸 수 없다. 실제 오류와 현재 셸 설정을 확인하고, 임시 파일 실험으로 새 파일·추가 쓰기·명시적 우회의 차이를 비교해 출력 방식을 고른다.
Bash에서 쓰기 가능한 파일에 >로 출력했는데 cannot overwrite existing file이 나오면, 실패한 셸의 noclobber 설정을 확인한다. 이 옵션이 켜져 있으면 이미 존재하는 일반 파일로의 덮어쓰기를 Bash가 거절한다. 파일에 쓰기 권한이 있어도 이 보호 설정은 별도로 적용된다.
GNU Bash의 리다이렉션 설명 (새 탭에서 열림)은 >의 대상이 없으면 파일을 만들고, 대상이 있으면 내용을 비운 뒤 쓰도록 설명한다. noclobber가 켜진 기존 일반 파일은 이 덮어쓰기 경로에서 거절된다. 오류 문구를 보지 않고 권한부터 넓히면 원인을 놓칠 수 있다.
실패한 명령과 같은 Bash에서 확인한다
다음은 Linux의 Bash와 GNU stat를 쓰는 읽기 전용 확인이다. ./result.txt를 실제 출력 대상 경로로 바꾼다. 스크립트에서 실패했다면 문제 줄 바로 앞에 넣어 그 프로세스의 상태를 확인한다.
printf 'Bash=%s\n' "${BASH_VERSION-}"
set -o | grep '^noclobber[[:space:]]'
stat -c 'type=%F mode=%A owner=%U' -- ./result.txt
별도 실험에서는 Bash 버전이 5.2.21(1)-release, 옵션이 noclobber on, 대상 종류가 regular file이었다. 파일 소유자는 실행 사용자였고 test -w도 성공했지만, >는 종료 코드 1과 위 오류를 반환했다. 같은 조건에서 옵션을 끈 대조군은 덮어쓰기에 성공했다.
첫 줄의 버전 값이 비어 있으면 먼저 실제 실행 셸부터 확인한다. 새 Bash를 열어 옵션을 조회하면 실패했던 프로세스의 설정을 놓칠 수 있다. noclobber가 off이거나 오류가 Permission denied라면, 이 설정만으로 증상을 설명할 수 없다. 파일·디렉터리 권한 등 실제 오류에 맞는 원인을 더 확인해야 한다.
빈 임시 파일도 이미 존재하는 파일이다
mktemp는 이름만 알려 주는 명령이 아니라 빈 파일을 만든다. 따라서 방금 만든 0바이트 일반 파일도 noclobber의 거절 대상이었다. 보호 여부는 기존 내용의 길이로 결정되지 않는다.
아래 코드는 새 임시 디렉터리 안에서만 기존 파일, 아직 없는 경로, 추가 쓰기를 비교한다. Bash와 mktemp, cat이 필요하다. 괄호 안에서만 옵션을 바꾸며, 의도한 실패 뒤에도 내용을 읽기 위해 set +e를 사용한다. 만든 임시 파일은 남는다.
(
set +e
set +o noclobber
lab=$(mktemp -d) || exit 1
f=$(mktemp "$lab/existing.XXXXXX") || exit 1
printf 'old\n' > "$f"
set -o noclobber
printf 'new\n' > "$f"
printf 'overwrite=%s\n' "$?"
printf 'first\n' > "$lab/fresh.txt"
printf 'create=%s\n' "$?"
printf 'tail\n' >> "$f"
printf 'append=%s\n' "$?"
cat -- "$f" "$lab/fresh.txt"
)
실제로 실행했을 때 덮어쓰기 줄에서 cannot overwrite existing file이 나왔고, 표준 출력은 다음과 같았다. 오류 앞의 스크립트 이름·줄 번호·임시 경로는 실행 방식에 따라 달라진다.
overwrite=1
create=0
append=0
old
tail
first
new는 기록되지 않았고 기존의 old가 남았다. >>로 쓴 tail만 그 뒤에 붙었다. 마지막의 first는 별도로 생성한 fresh.txt의 내용이다. 별도 대조 실험에서는 noclobber off일 때 기존 old가 new로 교체됐으며, 새 경로 생성과 추가 쓰기도 모두 종료 코드 0이었다. on일 때 거절된 것은 기존 일반 파일을 대상으로 한 >였다.
보존할지, 추가할지, 교체할지 결정한다
기존 결과를 보존해야 한다면 noclobber를 유지하고 아직 존재하지 않는 새 출력 경로를 고른다. 새 이름이 정말 비어 있는 이름인지는 실행 결과로 확인한다. 미리 touch나 mktemp로 파일부터 만들면 그 파일도 이미 존재하므로 같은 거절을 만날 수 있다.
기존 내용 뒤에 기록을 더하려는 작업이면 >>가 맞는다. 기존 내용을 교체하려던 명령을 단지 성공시키려고 >>로 바꾸면 이전 결과와 새 결과가 섞인다. 위 실험의 old와 tail처럼 두 내용이 함께 남아도 되는지 먼저 판단한다.
set 내장 명령 문서 (새 탭에서 열림)에서 noclobber는 -C와 같은 옵션이다. set -o noclobber로 켜고 set +o noclobber로 끈다. 현재 셸에서 끄면 이후의 다른 >에도 영향을 주므로, 오류를 없애는 목적으로 무조건 해제하지 않는다.
>|는 해당 리다이렉션에서 이 보호를 우회한다. 별도로 새로 만든 자기 소유의 일반 파일에 old를 넣고 >|로 replacement를 썼을 때 종료 코드는 0이었고, 내용이 교체된 뒤에도 noclobber는 on이었다. 기존 내용을 버려도 되는 자기 파일임을 확인한 경우에만 검토할 선택이다. 파일 권한 같은 다른 거절 조건까지 없애 주지는 않는다.
이 비교는 WSL2 Ubuntu 24.04의 Bash 5.2.21에서, Windows 드라이브를 연결한 v9fs 위에 새로 만든 일반 파일로 실행했다. 확인한 GNU 온라인 매뉴얼은 5.3판이며, 위 동작은 설치된 5.2.21에서 따로 관찰했다. 심볼릭 링크·장치 파일·FIFO, 다른 파일시스템과 다른 셸의 동작은 이번에 검증하지 않았다.