파일 교체 전에 다운로드 검증하기

ShellBeginner
지금 연습하기

소개

Field Notes는 어떤 자료가 손상되거나 누락되었는지 알고 있습니다. 이제 운영팀은 해당 자료만 가져와 독립적인 기준과 바이트를 비교하고, 검증을 통과한 뒤에만 각 교체를 확정하는 복구 절차가 필요합니다. 제공처는 HTTP 오류를 반환할 수도 있고, HTTP는 완전히 성공했지만 문서가 잘못된 응답을 반환할 수도 있습니다.

이 실습은 별도로 준비된 팩에서 시작합니다. 이전 실습에서 배운 SHA256, 전체 줄, 명령 치환, 전체 파일 이름에 대한 지식이 필요하지만 이전 VM의 파일은 필요하지 않습니다. Workspace와 Download Desk는 하나의 컨테이너를 공유합니다. 직접 만든 합성 PDF와 curl은 준비되어 있습니다. 시간은 읽기와 문제 해결을 포함한 작성자의 추정치입니다.

교체 전에 후보 다운로드하기

이 단계에서는 별도의 연습 디렉터리에 실제 배포 자료를 저장하고 검증합니다.

UTF-8 텍스트를 사용할 수 있도록 준비된 Workspace에서 시작합니다. 상대 파일 이름을 찾을 수 없으면 이 절대 경로를 사용합니다. pwd와 ls pack을 사용하면 디렉터리 오류와 이름의 오타를 구분할 수 있습니다.

cd /home/labex/project/download-desk

신뢰할 수 있는 매니페스트는 /opt/labex/download-pack-state/expected.sha256에 있습니다. 형식은 첫 실습과 같습니다. 소문자 64자리 16진수, 공백 두 개, 하위 경로가 없는 전체 파일 이름입니다. 독립적으로 제공되며 읽기 가능하고 비어 있지 않고 줄바꿈으로 끝납니다. 이름은 인쇄 가능한 ASCII를 사용하고 공백을 포함할 수 있습니다. 슬래시, 역슬래시, CR, LF, 탭 및 .과 ..이라는 이름은 범위 밖입니다. 부모 경로에 링크가 없는 실제 디렉터리를 사용합니다. 예상 항목은 없거나 일반 로컬 파일이어야 하며, 링크와 디렉터리 객체는 거부합니다. 기준과 제공처를 편집하지 마세요. SHA256 일치는 이 신뢰할 수 있는 기준과의 일치를 입증할 뿐, 게시자의 신원을 입증하지는 않습니다.

cat /opt/labex/download-pack-state/expected.sha256

준비된 trial 디렉터리는 실험용으로, 운영팀의 pack과 분리되어 있습니다. mktemp는 X 문자로 끝나는 템플릿을 사용해 고유한 빈 파일을 만들고 경로를 출력합니다. 이를 candidate에 저장하면 이 Workspace 세션에서 계속 사용할 수 있습니다. 다시 연결하면 셸 변수가 사라질 수 있습니다. 이전 값을 추측하지 말고 이 연습 순서를 처음부터 다시 시작하세요.

candidate=$(mktemp trial/.incoming.XXXXXX)

curl은 HTTP 요청을 보냅니다. 여기서 옵션은 서로 다른 역할을 합니다.

  • --get은 GET 요청을 보내고, --data-urlencode 'name=Workshop card.txt'는 파일 이름을 쿼리 매개변수로 안전하게 인코딩합니다. 따옴표는 셸 인수를 보호하고, URL 인코딩은 요청 안의 공백을 처리합니다.
  • --fail은 503 같은 HTTP 오류에서 0이 아닌 종료 상태를 만듭니다. HTTP 200에서 내용이 잘못된 경우는 감지하지 않습니다.
  • --silent --show-error는 진행 표시를 숨기고 오류 메시지는 유지합니다. --max-time 8은 전체 전송을 8초로 제한합니다.
  • --output "$candidate"는 임시 후보에만 씁니다. 127.0.0.1:8080은 같은 컨테이너에 준비된 로컬 제공처이며 /good은 올바른 바이트를 반환합니다.
curl --fail --silent --show-error --get --data-urlencode 'name=Workshop card.txt' --max-time 8 --output "$candidate" http://127.0.0.1:8080/good
printf 'Download exit: %s\n' "$?"

예상 결과는 Download exit: 0입니다. URL을 잘못 입력하면 curl이 연결 오류나 HTTP 거부를 보고할 수 있습니다. 위 주소를 고치고 rm -- "$candidate"로 현재 후보만 삭제한 뒤 mktemp부터 반복합니다. --는 옵션의 끝이며, 정확한 변수 값은 자신의 실험 파일을 가리킵니다.

신뢰할 수 있는 매니페스트의 해당 줄에서 배포 자료의 예상 해시를 가져옵니다. 후보와 독립적인 다음 값입니다.

expected=a9986fb03d5ca72e42753d0ddd11dce7794b0e5a8b253dd4cd356825ca6d0583

파이프 |는 한 명령의 표준 출력을 다음 명령의 표준 입력에 연결합니다. 다음 printf는 후보의 체크섬 항목을 만듭니다. sha256sum --check가 이를 읽고, &&는 검증 성공 후에만 mv를 실행합니다. mv --는 검증된 후보를 최종 연습 파일로 이름 변경합니다. 이 파일은 실험을 반복할 때 교체해도 됩니다. 운영팀의 선택적 복구 규칙은 pack에 적용됩니다.

printf '%s  %s\n' "$expected" "$candidate" | sha256sum --check && mv -- "$candidate" "trial/Workshop card.txt"

: OK로 끝나는 줄을 예상합니다. FAILED라면 후보를 옮기지 말고 그 파일만 삭제한 뒤 수정된 다운로드를 반복합니다. 이제 검증한 배포 자료를 읽을 수 있습니다.

cat "trial/Workshop card.txt"
REPAIR DESK: EAST HALL

검증하지 않은 다운로드를 최종 파일 이름과 분리했습니다. 다음으로 이 순서를 팩 전체에 적용합니다.

선택적으로 복구하고 실패 후 이어서 진행하기

이 단계에서는 절차에 세 인수를 부여하고, 유용한 결과와 실패 시 보호 범위를 모두 확인합니다.

Workspace의 /home/labex/project/download-desk에서 시작합니다. 스크립트 호출 규약은 bash repair-pack.sh MANIFEST SOURCE_URL PACK_DIRECTORY입니다. 이 인수와 설치된 명령을 사용하는 독립 Bash 프로그램이며 옆의 보조 파일은 필요하지 않습니다. 먼저 각 항목의 실제 바이트를 분류해야 합니다. 일치하는 항목은 다운로드하지 않고 바이트, 모드, 소유자, mtime을 유지합니다. 무관한 파일 pack/notes.txt도 똑같이 보호합니다. 누락되거나 불일치하는 항목만 가져올 수 있으며, 한 번 호출할 때 각 항목은 최대 한 번만 다운로드합니다. 해시가 일치하는 빈 파일은 올바른 파일입니다. 완성된 팩에서 두 번째 호출은 다운로드 요청 0건으로 팩의 전체 상태를 유지해야 합니다.

각 후보는 대상과 나란히 같은 디렉터리에 두며 정상 성공이나 보고된 실패 뒤에 삭제합니다. 확정은 파일 단위입니다. 나중 항목이 실패해도 앞서 검증된 교체 결과는 남아 있어도 됩니다. 실패했거나 잘못된 후보는 대상을 절대 교체하지 않습니다. 이는 팩 전체 트랜잭션이 아니며, 동시 쓰기, 정전, 가능한 모든 프로세스 종료에 대한 보장도 아닙니다. 과정 경로만 사용하고 동시 변경을 피하세요.

파서와 해시 추출에는 이전 실습의 구문을 사용합니다. 다음 요소는 교체를 보호합니다.

  • [ -r "$manifest" ]는 읽기 가능 여부를 확인하고 [ -s "$manifest" ]는 비어 있지 않은 매니페스트를 요구합니다. !는 검사를 반대로 바꿉니다. ||는 검사 사이에서 OR를 뜻하거나 명령 실패 후 오류 분기를 실행합니다. 여기서 &&는 AND를 뜻합니다. 중괄호는 새 서브셸 없이 관련 검사를 묶습니다.
  • [ -d "$pack" ]은 디렉터리를 검사합니다. [ -L "$target" ]은 대상이 사라진 링크도 감지합니다. -e는 대상 존재를, -f는 일반 파일을 확인합니다. mv 전에 링크와 다른 객체 유형을 거부하면 원본으로 연결되는 링크를 따라가거나 후보를 디렉터리 안에 잘못 옮기는 일을 막습니다.
  • temporary=는 빈 변수로 시작합니다. trap '...' EXIT는 스크립트가 끝날 때 정리할 작업을 등록합니다. 작은따옴표는 정리 시점까지 확장을 미룹니다. -n은 변수가 비어 있지 않다는 뜻이며 스크립트가 직접 만든 파일만 삭제합니다. rm -f는 이미 삭제된 후보도 오류 없이 처리하며 --는 옵션의 끝입니다.
  • continue는 다음 매니페스트 줄로 이동합니다. exit 1은 실패를 보고합니다. >&2는 설명을 일반 출력 대신 표준 오류로 보냅니다. 이름 변경에 성공한 뒤 temporary를 비우면 정리 작업이 확정된 파일을 건드리지 않습니다.

첫 실습에서 배운, 내용을 그대로 저장하는 히어 도큐먼트로 다음 전체 스크립트를 저장합니다.

cat > repair-pack.sh <<'BASH'
#!/usr/bin/env bash
manifest=$1
base=$2
pack=$3
if [ ! -r "$manifest" ] || [ ! -s "$manifest" ]; then
    printf 'A readable nonempty trusted manifest is required.\n' >&2
    exit 1
fi
if [ ! -d "$pack" ] || [ -L "$pack" ]; then
    printf 'A real pack directory is required.\n' >&2
    exit 1
fi
temporary=
trap 'if [ -n "$temporary" ]; then rm -f -- "$temporary"; fi' EXIT
while IFS= read -r line; do
    expected=${line:0:64}
    name=${line:66}
    target=$pack/$name
    if [ -L "$target" ] || { [ -e "$target" ] && [ ! -f "$target" ]; }; then
        printf 'Expected a regular file or absent member: %s\n' "$name" >&2
        exit 1
    fi
    if [ -f "$target" ]; then
        actual=$(sha256sum -- "$target")
        actual=${actual%% *}
        if [ "$actual" = "$expected" ]; then
            printf 'Keep: %s\n' "$name"
            continue
        fi
    fi
    temporary=$(mktemp "$pack/.repair.XXXXXX") || exit 1
    if ! curl --fail --silent --show-error --get --data-urlencode "name=$name" --max-time 8 --output "$temporary" "$base"; then
        printf 'Download failed: %s\n' "$name" >&2
        exit 1
    fi
    actual=$(sha256sum -- "$temporary")
    actual=${actual%% *}
    if [ "$actual" != "$expected" ]; then
        printf 'Checksum mismatch: %s\n' "$name" >&2
        exit 1
    fi
    mv -- "$temporary" "$target" || exit 1
    temporary=
    printf 'Repaired: %s\n' "$name"
done < "$manifest"
BASH

정리 작업에는 자신이 만든 정확한 임시 경로만 전달되며 다른 사람의 파일은 삭제하지 않습니다. 다른 안전한 임시 이름이나 동등한 반복문도 허용됩니다. 검사는 동작을 평가하며 정확한 명령 표기나 Keep: 출력 문구를 평가하지 않습니다.

HTTP는 성공하지만 내용은 잘못된 응답 시험하기

제어된 /html 엔드포인트는 HTTP 200과 함께 HTML 오류 페이지를 반환합니다. 실제 응답이며 curl 상태를 흉내 낸 것이 아닙니다. 다음 명령은 기존의 손상된 배포 자료를 교체하지 않고 실패해야 합니다.

bash repair-pack.sh /opt/labex/download-pack-state/expected.sha256 http://127.0.0.1:8080/html pack
printf 'Repair exit: %s\n' "$?"

Repair exit: 1과 Checksum mismatch: Workshop card.txt를 예상합니다. PDF는 이미 올바르므로 건너뛰었습니다. 잘못된 후보는 삭제되었으며 HTTP 성공만으로 확정되지 않았습니다. ls -a는 숨겨진 이름도 표시하므로 .repair.* 후보가 남지 않았는지 확인할 수 있습니다.

ls -a pack

이제 올바른 제공처를 사용합니다. 스크립트는 PDF, 빈 감사 명단 파일, 개인 메모를 보존하면서 배포 자료와 누락 지도를 복구합니다.

bash repair-pack.sh /opt/labex/download-pack-state/expected.sha256 http://127.0.0.1:8080/good pack
(cd pack && sha256sum --check /opt/labex/download-pack-state/expected.sha256)

나열된 모든 항목에 OK가 표시되어야 합니다. Download Desk로 전환해 새로 고친 뒤 PDF와 지도를 엽니다. 지도에는 동쪽 입구가 적혀 있습니다. PDF 옆의 Download를 선택하고 저장한 파일을 브라우저 리더로 엽니다. 두 작업 모두 로컬 팩을 사용하며 제공처에서 보충하지 않습니다. 고정된 PDF 페이지에는 기본 확대/축소 기능을 사용합니다. Workspace로 돌아갑니다.

재다운로드 없이 반복하기

준비된 제공처는 실제 자료 요청마다 제공처 프로세스 식별 정보를 포함한 한 줄을 requests.jsonl에 기록합니다. 각 이벤트는 관찰되기 전에 파일에 완전히 기록됩니다. wc -l < FILE은 파일을 명령으로 리디렉션하여 줄 수를 셉니다. 자료를 요청하지는 않습니다. 카운터를 읽고 절차를 다시 실행한 다음 한 번 더 읽습니다.

wc -l < /opt/labex/download-pack-state/requests.jsonl
bash repair-pack.sh /opt/labex/download-pack-state/expected.sha256 http://127.0.0.1:8080/good pack
wc -l < /opt/labex/download-pack-state/requests.jsonl

두 숫자는 같아야 합니다. 네 항목 모두 유지되어야 합니다. 전부 다시 다운로드하면 요청을 낭비하며, 최종 해시가 같아도 올바른 파일의 모드나 타임스탬프가 바뀔 수 있습니다.

완성된 팩 밖에서 전송 실패 연습하기

mkdir -p는 필요할 때 실험 디렉터리를 만듭니다. 디렉터리를 초기화하지는 않습니다. 운영팀의 완성된 팩은 그대로 둡니다.

mkdir -p trial/fault-test

제어된 /error 엔드포인트는 HTTP 503을 반환합니다. 503이 포함된 curl 오류와 0이 아닌 스크립트 상태를 예상합니다.

bash repair-pack.sh /opt/labex/download-pack-state/expected.sha256 http://127.0.0.1:8080/error trial/fault-test
printf 'HTTP-error exit: %s\n' "$?"

예상 결과는 HTTP-error exit: 1입니다. 제어된 /partial 엔드포인트는 선언한 본문 중 일부만 전송한 뒤 연결을 끊습니다. 아직 남은 바이트에 대한 curl 오류 18이 발생하며, 이번에도 후보가 확정되지 않아야 합니다.

bash repair-pack.sh /opt/labex/download-pack-state/expected.sha256 http://127.0.0.1:8080/partial trial/fault-test
printf 'Partial-transfer exit: %s\n' "$?"

예상 결과는 Partial-transfer exit: 1입니다. 연결 실패, HTTP 거부, HTTP 200의 잘못된 내용은 서로 다른 관찰이지만 모두 미검증 교체를 막습니다. 같은 올바른 엔드포인트와 인수로 실험을 복구합니다.

bash repair-pack.sh /opt/labex/download-pack-state/expected.sha256 http://127.0.0.1:8080/good trial/fault-test

매니페스트나 디렉터리 경로를 잘못 입력하면 스크립트는 필요한 객체를 알려 주고 0이 아닌 상태로 종료합니다. 재시도 전 pwd, ls, 기준의 절대 경로를 확인합니다. 저장한 스크립트에 구문 오류가 있으면 위의 전체 히어 도큐먼트로 repair-pack.sh만 교체합니다. 오타를 고치려고 운영팀 팩을 삭제하지 마세요.

최종 검사는 실제 팩과 보존 대상을 읽습니다. 또한 다른 이름과 제어된 HTTP 실패가 있는 격리된 팩에서 수정하지 않은 스크립트를 실행합니다. 기존 손상 대상과 정당한 빈 파일이 누락된 경우도 포함됩니다. 이 요청은 자신의 스크립트가 보내는 것이며 채점용 다운로드가 누락된 증거를 보충하지 않습니다. 검사를 반복해도 실제 작업을 복구하거나 초기화하지 않습니다.

요약

후보를 다운로드하고 확정 전에 SHA256을 확인한 뒤 재사용 가능한 선택적 복구 스크립트를 만들었습니다. 실제 HTTP 오류, 내용이 잘못된 성공 응답, 중단된 응답 본문이 최종 파일 이름에 들어가지 못하게 했습니다. 올바른 파일과 개인 메모를 보존했으며, 완성된 팩에서 두 번째 실행에는 다운로드가 필요하지 않았습니다.