작업 스케줄러에서만 파일을 못 찾을 때: 시작 위치와 PowerShell 경로 고치기
터미널에서는 잘 돌아가는데 작업 스케줄러에만 넣으면 config.json을 못 찾는다. 파일을 다시 만들어도, 스크립트를 다시 저장해도 똑같다. 이때 먼저 볼 것은 파일의 존재가 아니라 어느 폴더를 기준으로 찾고 있는가다.
이 글은 Windows PowerShell 스크립트의 상대 경로 문제를 다룬다. 실행 자체가 안 되는 모든 오류의 해결책은 아니다. 실행 계정·권한·네트워크 문제는 마지막 체크리스트에서 구분한다.
파일은 같은데 왜 실행 결과가 달라질까
.\config.json의 점은 ‘지금 실행 중인 ps1 파일이 있는 곳’이라는 뜻이 아니다. PowerShell의 현재 위치를 기준으로 한다. 반면 $PSScriptRoot는 실행 중인 스크립트가 들어 있는 폴더를 가리킨다. 따라서 -File 뒤에 스크립트의 절대 경로를 넣어도, 본문 속 상대 경로까지 자동으로 바뀌지는 않는다.
상대 경로
현재 위치 → .\config.json
출발 폴더가 바뀌면 찾는 파일도 달라짐
스크립트 기준 경로
ps1이 있는 폴더 → Join-Path $PSScriptRoot 'config.json'
출발 폴더가 달라도 같은 파일을 찾음
직접 재현한 결과: 폴더만 바꿔도 실패했다
2026년 9월 8일 Windows 환경의 powershell.exe를 -NoProfile -NonInteractive -File로 실행했다. 테스트용 JSON과 ps1을 같은 폴더에 두고, 자식 PowerShell을 시작하는 위치와 파일 경로 작성 방식만 바꿨다. 민감한 설정 파일 대신 FMNOTE-DEMO-ONLY라는 표시가 든 JSON을 사용했다.
| 경로 작성 방식 | 실행 시작 폴더 | 결과 / 종료 코드 |
|---|---|---|
| 상대 경로 | ps1과 같은 폴더 | CONFIG_OK / 0 |
| 상대 경로 | 그 위 폴더 | ItemNotFoundException / 1 |
| PSScriptRoot 기준 | ps1과 같은 폴더 | CONFIG_OK / 0 |
| PSScriptRoot 기준 | 그 위 폴더 | CONFIG_OK / 0 |
검증 범위: 이 실험은 작업 폴더 차이로 인한 파일 읽기 실패를 재현한 것이다. 실제 작업 스케줄러에 작업을 등록하거나, 로그오프 상태·다른 계정·네트워크 드라이브까지 시험한 결과는 아니다. 종료 코드 0과 1은 아래 테스트 코드에서 직접 지정했다.
수정 1. 스크립트 옆 파일은 PSScriptRoot 기준으로 읽기
아래 코드를 probe.ps1로 저장한다. 같은 폴더의 config.json에는 다음 내용만 넣는다.
{"marker":"FMNOTE-DEMO-ONLY"}
이 스크립트는 -Fixed가 없으면 상대 경로를, 있으면 스크립트 기준 경로를 사용한다.
param([switch]$Fixed)
$ErrorActionPreference = 'Stop'
try {
$configPath = if ($Fixed) {
Join-Path $PSScriptRoot 'config.json'
} else {
'.\config.json'
}
$config = Get-Content -LiteralPath $configPath -Raw |
ConvertFrom-Json
if ($config.marker -ne 'FMNOTE-DEMO-ONLY') {
throw 'Unexpected test input'
}
Write-Output 'CONFIG_OK'
exit 0
} catch {
Write-Output ('CONFIG_ERROR:' + $_.Exception.GetType().Name)
exit 1
}
예를 들어 두 파일을 C:\Jobs\PathDemo에 저장했다면 그 폴더와 상위 폴더에서 각각 다음 명령을 실행해 차이를 확인할 수 있다. 예시 경로는 자신의 테스트 폴더로 바꾼다.
powershell.exe -NoProfile -NonInteractive -File "C:\Jobs\PathDemo\probe.ps1"
$LASTEXITCODE
powershell.exe -NoProfile -NonInteractive -File "C:\Jobs\PathDemo\probe.ps1" -Fixed
$LASTEXITCODE
실무 코드에는 분기 전체가 필요 없다. $configPath = Join-Path $PSScriptRoot 'config.json'처럼 기준을 고정하고 읽으면 된다. 다만 이 예시는 ps1 파일 안에서 사용하는 방식이다. 대화형 콘솔에 한 줄씩 붙여 넣는 것과는 조건이 다르다.
수정 2. 작업 스케줄러의 세 칸을 분리하기
작업 속성의 ‘동작’에서 실행 항목을 편집한다. 아래는 C:\Jobs\PathDemo에 테스트 파일을 둔 경우의 예시다. 운영 중인 작업은 먼저 기존 설정을 기록하고, 테스트용 복제 작업에서 확인하는 편이 안전하다.
| 입력 칸 | 예시 값 |
|---|---|
| 프로그램/스크립트 | C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe |
| 인수 추가 | -NoProfile -NonInteractive -File "C:\Jobs\PathDemo\probe.ps1" -Fixed |
| 시작 위치 | C:\Jobs\PathDemo |
‘시작 위치’에는 파일명이 아닌 폴더를 넣는다. Windows나 PowerShell이 다른 곳에 설치돼 있다면 실행 파일 경로부터 실제 값으로 바꿔야 한다. PowerShell 7의 pwsh.exe를 사용하는 작업에 Windows PowerShell 경로를 그대로 덮어쓰지도 말자.
시작 위치를 지정하면 상대 경로를 쓰는 다른 코드나 외부 도구에도 일관된 출발점을 줄 수 있다. 하지만 외부 프로그램이 자기 설정 안의 경로를 해석하는 방식까지 모두 고쳐 주는 만능 설정은 아니다. 스크립트가 직접 읽는 파일은 경로도 명시적으로 만드는 편이 분명하다.
그래도 실패한다면, 권한부터 무작정 올리지 말기
- 파일을 못 찾음: 실제로 해석된 경로와 파일명을 먼저 비교한다. 수동 실행과 예약 실행의 현재 위치가 같은지 확인한다.
- 접근 거부: 작업 실행 계정이 해당 폴더를 읽고 결과 폴더에 쓸 수 있는지 확인한다. 관리자 실행을 먼저 켜는 것으로 원인 확인을 대신하지 않는다.
- 네트워크 파일만 실패: 로그인한 사용자에게 보이는 드라이브가 예약 실행 계정에도 보이는지 따로 확인한다. 이번 로컬 파일 실험으로 네트워크 접근을 보장할 수는 없다.
- 실행 정책·모듈 오류: 경로 문제와 별개다. 오류 메시지를 확인하고 필요한 실행 환경을 맞춘다. 이 글의 예제는 실행 정책 우회를 사용하지 않는다.
- 완료 표시만 있고 결과 없음: 마지막 실행 결과뿐 아니라 예상 출력 파일의 존재·수정 시각·내용도 확인한다. 예제의 CONFIG_OK는 설정 파일 읽기에 성공했다는 뜻이지 본 작업 전체의 성공을 뜻하지 않는다.
운영 로그에는 설정 파일 원문이나 토큰을 남기지 않는다. 확인에 필요한 단계명·오류 종류·종료 코드부터 기록하고, 로컬 경로에도 사용자명 같은 정보가 포함될 수 있다는 점을 기억하자.
정리: ‘어디서 실행해도 같은 파일’이 되게 만들기
터미널에서 한 번 성공한 것은 그 실행 조건에서 성공했다는 뜻이다. 예약 실행에서도 같은 파일을 읽게 하려면 시작 위치를 점검하고, 스크립트가 쓰는 파일 경로를 의도한 기준으로 고정해야 한다. 수정 후에는 실제 예약 작업의 실행 계정과 실행 조건에서도 결과물을 다시 확인하자.
공식 문서와 검증 기준
- Microsoft Learn: PowerShell 자동 변수 — PSScriptRoot, PWD, LASTEXITCODE
- Microsoft Learn: 작업 스케줄러 WorkingDirectory 속성
공식 문서 확인 및 로컬 재현: 2026-09-08. 이 글은 광고·제휴 링크 없이 경로 오류를 진단하는 기술 가이드다.
