빌드노트
sed -i 뒤에 심볼릭 링크가 일반 파일이 됐다
GNU sed -i는 심볼릭 링크가 있던 이름을 새 일반 파일로 교체할 수 있다. 링크·대상의 종류와 inode·내용을 비교하고, --follow-symlinks로 대상을 편집할지 새 파일로 결과를 남길지 고른다.
심볼릭 링크 경로에 GNU sed -i를 실행하면, 그 경로에서는 바뀐 내용이 보이는데 원래 대상 파일은 그대로일 수 있다. 기본 -i가 링크를 따라 원래 파일에 덮어쓰는 대신, 링크가 있던 이름을 새 일반 파일로 교체하기 때문이다. 링크를 유지하며 대상을 편집하려면 아직 링크인 경로에 --follow-symlinks를 함께 쓴다.
GNU sed 옵션 문서 (새 탭에서 열림)는 -i가 임시 파일에 결과를 쓰고, 입력을 다 읽으면 원래 출력 이름으로 이름을 바꾼다고 설명한다. 기본 동작에서는 명령행에 지정한 심볼릭 링크가 교체되고 대상은 수정되지 않는다. 여기서 in-place는 같은 이름에 편집 결과가 남는다는 뜻이지, 같은 inode에 계속 쓴다는 보장이 아니다.
링크였던 경로와 원래 대상을 따로 확인한다
다음 조회는 파일을 수정하지 않는다. Linux Bash와 GNU coreutils를 전제로 하며, p는 문제가 난 경로로, ./target.conf는 변경 전 링크가 가리키던 실제 파일 경로로 바꾼다. 원래 대상은 배포 설정이나 변경 전 기록에서 확인해야 한다. 이미 링크가 사라졌다면 현재 경로를 따라가는 것만으로 과거 대상을 알아낼 수는 없다.
p='./active.conf'
LC_ALL=C stat -c '%n type=%F dev=%d inode=%i' -- "$p" ./target.conf
readlink -e -- "$p"
cat -- "$p" ./target.conf
stat은 기본적으로 링크 자체를 조회하고, stat -L은 링크를 따라 대상을 조회한다. 위 명령에서는 링크 자체의 종류를 보려고 -L을 넣지 않았다. type=symbolic link면 링크가 남아 있고, type=regular file이면 현재 그 이름은 일반 파일이다. dev는 파일시스템 장치 번호, inode는 그 안의 파일 식별 번호다. 파일을 비교할 때는 두 값을 함께 본다. GNU stat 문서 (새 탭에서 열림)
readlink -e는 모든 경로 요소가 존재할 때 링크들을 따라간 최종 절대경로를 출력한다. 일반 파일에도 성공하므로, 이 명령의 성공을 링크가 남았다는 뜻으로 읽으면 안 된다. 출력 없이 실패했다면 대상 누락이나 경로 접근 문제 등을 먼저 확인한다. 링크에 저장된 문자열 자체는 옵션 없는 readlink -- "$p"로 읽는다. 일반 파일에 실행하면 출력 없이 실패한다. GNU readlink 문서 (새 탭에서 열림)
같은 내용을 읽었지만 교체한 이름이 달랐다
실제 장애 기록 대신, 새 임시 폴더에 mode=old 한 줄을 담은 target.conf와 target.conf를 가리키는 active.conf 링크를 만들어 비교했다. 각 조건은 별도 폴더에서 같은 입력으로 시작했고, 대상 내용은 새 일반 파일 target.original에도 복사해 보관했다. 다음 수치는 GNU sed 4.9에서 직접 조회한 값이며 재실행하면 번호는 달라진다.
기본 sed -i 's/old/new/' active.conf 실행 전에는 링크의 inode가 281474988497017, 대상의 inode가 281474988497015였다. 실행 뒤 종류·식별 번호는 다음과 같았다. 두 항목의 dev는 모두 71이었다.
./active.conf type=regular file dev=71 inode=562949965207597
./target.conf type=regular file dev=71 inode=281474988497015
링크가 있던 이름의 종류와 inode가 바뀌었고, 대상 inode는 유지됐다. 앞 조회의 cat은 두 파일을 지정한 순서대로 읽으므로 내용 차이도 보였다.
mode=new
mode=old
이 상태에서 readlink -e의 결과는 active.conf 자체의 절대경로였다. 옵션 없는 readlink는 출력 없이 종료 코드 1을 냈다. 이름은 그대로여도 더 이상 target.conf로 이어지지 않는 것이다. 문서의 임시 파일 교체 설명과 이 관찰이 일치한다.
별도 조건에서 sed --follow-symlinks -i 's/old/new/' active.conf를 실행하자 링크 종류와 inode 281474988497020은 유지됐다. 반면 대상 inode는 562949965207675에서 281474988497022로 바뀌었고, 두 경로 모두 mode=new를 읽었다. 이 옵션으로 유지한 것은 심볼릭 링크이며, 대상의 기존 inode까지 유지한 것은 아니다.
원래 대상을 바꿀지, 별도 결과를 남길지 고른다
원래 대상 파일의 내용을 바꾸려면 먼저 링크가 남아 있는지, 최종 경로가 수정하려는 파일인지 확인한다. sed --version에서 GNU sed인지도 확인한다. 변경 전 내용은 아직 없는 별도 이름에 cp -- ./target.conf ./target.original로 보관할 수 있다. 링크를 복사하는 대신 확인한 대상 파일을 복사한다.
아래는 여전히 심볼릭 링크인 p를 통해 대상을 편집하고 결과와 백업을 읽는 명령이다. 앞에서 정한 p를 사용하며, 예제의 대상은 ./target.conf다. target.conf.bak도 아직 없는 이름이어야 한다. 치환식 s/old/new/는 각 줄에서 처음 나오는 old를 new로 바꾸므로 실제 파일에 맞게 바꾼다. 쓰기 전 sed 's/old/new/' "$p"로 출력만 살펴볼 수 있다. 오류가 나면 뒤의 확인을 성공으로 간주하지 말고 경로와 파일·디렉터리 권한부터 확인한다.
sed --follow-symlinks -i.bak 's/old/new/' "$p" &&
LC_ALL=C stat -c '%n type=%F dev=%d inode=%i' -- "$p" ./target.conf &&
cat -- "$p" ./target.conf ./target.conf.bak
직접 실행한 이 조건에서도 active.conf는 링크로 남았고 대상은 새 inode로 교체됐다. 마지막 명령의 출력은 mode=new, mode=new, mode=old 순서였다. 이 배치에서는 백업 이름이 active.conf.bak이 아니라 실제 대상 옆의 target.conf.bak이었다. --follow-symlinks는 링크를 지원하는 플랫폼에서 -i와 함께 쓸 때 최종 대상을 편집한다. 백업 접미사의 의미도 같은 GNU sed 문서 (새 탭에서 열림)에 설명돼 있다.
대상을 그대로 두고 변환 결과만 필요하면 -i를 빼고 새 파일로 출력한다. 다음은 원본 링크와 대상을 변경하지 않는 선택이다. p가 아직 mode=old인 대상을 가리키고, 자신이 관리하는 출력 폴더에 rendered.conf라는 항목이 없는 조건에서 실행한다.
(set -C; sed 's/old/new/' "$p" > ./rendered.conf) &&
cat -- ./rendered.conf
이 조건에서는 출력 파일만 mode=new였고 링크·대상의 inode와 mode=old 내용은 모두 유지됐다. 괄호 안의 set -C는 Bash의 noclobber 설정으로, 기존 일반 파일에 대한 > 덮어쓰기를 거부한다. 같은 출력 이름으로 다시 쓰는 대조에서는 종료 코드 1과 cannot overwrite existing file을 확인했고 기존 결과도 유지됐다. 입력 파일과 같은 이름으로 >를 쓰면 셸이 sed 실행 전에 파일을 비울 수 있으므로 서로 다른 이름을 쓴다. Bash 리다이렉션 문서 (새 탭에서 열림)
이미 active.conf가 일반 파일로 바뀌었다면 --follow-symlinks를 뒤늦게 추가해도 예전 연결이 복원되지는 않는다. 현재 편집 결과를 보관하고 변경 전 대상 경로를 확인한 다음, 대상에 반영할 내용과 링크를 다시 만들 필요를 따로 판단한다. 링크가 남은 경우에 쓸 편집 명령을 이미 교체된 파일에 반복하지 않는다.
실행 환경은 WSL2 Ubuntu 24.04.4, Bash 5.2.21, GNU sed 4.9, GNU coreutils 9.4이며 실습 파일시스템은 Windows 드라이브를 연결한 v9fs였다. 온라인 GNU sed 매뉴얼은 4.10을 설명하므로, 실험 버전인 GNU sed 4.9 공식 배포본 (새 탭에서 열림)의 문서에서도 두 옵션의 설명을 대조했다. --follow-symlinks를 BSD sed나 macOS 기본 sed에도 적용할 수 있다고 가정하지 않는다. ext4·NFS에서의 별도 실행, 다른 프로세스의 동시 경로 교체, 이미 없어진 링크의 재생성은 검증하지 않았다.