I cobbled together the following batch file from googling around but definitely don’t understand it at a deep level. It’s purpose is to access the URL for the daily NYT crossword PDF and pass along my authentication cookie to download it into a local directory:
- DropboxDocumentsProjectsProgrammingnyt_dailypdfs
- 2024_07_26.pdf
- 2024_07_27.pdf
- 2024_07_28.pdf
- 2024_07_29.pdf
It runs fine when double clicked but when scheduled with the task scheduler I just get:
The operation completed successfully. (0x0)
But there is no PDF file produced.
Here’s the batch code:
@echo off
setlocal
:: Get the current date and time
for /f "tokens=2 delims==" %%I in ('"wmic os get localdatetime /value"') do set "datetime=%%I"
:: Extract year, month, and day
set "year=%datetime:~0,4%"
set "month=%datetime:~4,2%"
set "day=%datetime:~6,2%"
:: Convert month number to month abbreviation
set "monthname="
for %%m in (01 02 03 04 05 06 07 08 09 10 11 12) do (
if "%month%"=="%%m" set "monthname=%%m"
)
if "%month%"=="01" set "monthname=Jan"
if "%month%"=="02" set "monthname=Feb"
if "%month%"=="03" set "monthname=Mar"
if "%month%"=="04" set "monthname=Apr"
if "%month%"=="05" set "monthname=May"
if "%month%"=="06" set "monthname=Jun"
if "%month%"=="07" set "monthname=Jul"
if "%month%"=="08" set "monthname=Aug"
if "%month%"=="09" set "monthname=Sep"
if "%month%"=="10" set "monthname=Oct"
if "%month%"=="11" set "monthname=Nov"
if "%month%"=="12" set "monthname=Dec"
:: Format year to two digits
set "year_short=%year:~2,2%"
:: Combine into desired format
set "formatted_date=%monthname%%day%%year_short%"
set fileNameStr=%year%_%month%_%day%
:: Output the result
echo %formatted_date%
:: Define the URL of the file you want to download
:: Replace this URL with the actual URL of the file
set CROSSWORD_URL=https://www.nytimes.com/svc/crosswords/v2/puzzle/print/%formatted_date%.pdf
echo %CROSSWORD_URL%
:: Define the filename for the downloaded file
set OUTPUT_FILE="pdfs%fileNameStr%.pdf"
:: Define the path to the cookies file
set COOKIES_FILE="nyt-cookies.txt"
:: Use curl to download the file using the cookies file
echo Downloading file using cookies...
curl -b %COOKIES_FILE% -o %OUTPUT_FILE% %CROSSWORD_URL%
:: Check if the download was successful
if %ERRORLEVEL% equ 0 (
echo Download completed successfully: %OUTPUT_FILE%
) else (
echo Failed to download the file.
)
endlocal
I’m guessing it’s some kind of local/working directory mismatch but am not sure where to solve it.
1
Change the lines of the batch file to:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Get the current date in a country/region independent format
for /F "tokens=1-3 delims=/ " %%G in ('%SystemRoot%System32robocopy.exe "%SystemDrive%|" . /NJH') do set "year=%%G" & set "month=%%H" & set "day=%%I" & goto GetMonthAbbr
:GetMonthAbbr
rem Convert month number to month abbreviation
for %%M in ("01 Jan" "02 Feb" "03 Mar" "04 Apr" "05 May" "06 Jun" "07 Jul" "08 Aug" "09 Sep" "10 Oct" "11 Nov" "12 Dec") do for /F "tokens=1,2" %%G in (%%M) do if %month% == %%G set "monthname=%%H" & goto FormatDate
:FormatDate
rem Combine into desired format with year with just two digits (no century)
set "formatted_date=%monthname%%day%%year:~2,2%"
set "fileNameStr=%year%_%month%_%day%"
echo %formatted_date%
rem Define the URL of the file to download.
set "CROSSWORD_URL=https://www.nytimes.com/svc/crosswords/v2/puzzle/print/%formatted_date%.pdf"
echo %CROSSWORD_URL%
rem Define the file name for the downloaded file.
set "OUTPUT_FILE=%~dp0pdfs%fileNameStr%.pdf"
for %%I in ("%OUTPUT_FILE%") do if not exist "%%~dpI" md "%%~dpI"
rem Define the path to the cookies file.
set "COOKIES_FILE=%~dp0nyt-cookies.txt"
rem Use curl to download the file using the cookies file.
echo Downloading file using cookies...
%SystemRoot%System32curl.exe -b "%COOKIES_FILE%" -o "%OUTPUT_FILE%" "%CROSSWORD_URL%"
rem Check if the download was successful.
if errorlevel 1 (
echo Failed to download the file from:
echo "%CROSSWORD_URL%"
) else (
echo Download completed successfully:
echo "%OUTPUT_FILE%"
)
endlocal
The two command lines
echo Download completed successfully:
echo "%OUTPUT_FILE%"
could be replaced by the single command line
for %%I in ("%OUTPUT_FILE%") do echo Download completed successfully: "%%~nxI"
to output just the file name with file extension of the downloaded PDF file instead of the full file name with path. I favor getting always output the fully qualified file/folder name by a batch file. Then I must not guess or find out by myself where the file/folder really is or is expected by the batch file.
The main fix is the definition of the environment variables OUTPUT_FILE
and COOKIES_FILE
with fully qualified file names by using %~dp0
which expands to full path of the batch file directory ending always with a backslash.
Which directory is set by explorer.exe
on calling the Windows kernel library function CreateProcess with function parameter lpCurrentDirectory
on starting %ComSpec%
with the option /c
and the fully qualified batch file name appended on double clicking on the batch file in Windows File Explorer does no longer matter now. The Windows Task Scheduler calls CreateProcessAsUser with function parameter lpCurrentDirectory
pointing to the string defined with scheduled task property Start in. There is used %SystemRoot%System32
as Start in directory if there is no directory defined in scheduled task properties.
All other files and folders are referenced also with fully qualified file name (= drive + path + name + extension) as in this case cmd.exe
does not need to search for any file using the local environment variables PATH
and PATHEXT
. That makes the batch file execution faster because of avoiding file system accesses. It makes the batch file execution also more fail-safe and secure because of not depending anymore on environment variable PATH
which is much too often modified to something not useful by users or installation scripts.
The first two command lines define the required execution environment completely without depending on settings defined outside of the batch file. Please read the issue chapters in this answer for more information.
There is used an error message output by ROBOCOPY to get the current date in a country/region independent format. WMIC is declared by Microsoft as deprecated and might be not installed anymore by default. WMIC is already an optional Windows component. See Compo’s or my answer on Time is set incorrectly after midnight for a detailed explanation of the FOR command line running ROBOCOPY.
See single line with multiple commands using Windows batch file for an explanation of the unconditional command operator &
used for multiple commands on one line without using a slower to process command block.
The first FOR loop in batch file in question is completely useless as it defines the environment variable monthname
with the same string as assigned to month
. That is the reason for all the IF conditions to get the appropriate month abbreviation. The batch file posted above shows how to use a FOR loop for getting the month abbreviation for the current month by using two FOR commands and exiting the loop on having found the matching month abbreviation with command GOTO. That long single line is executed faster than all the IF conditions.
There should be read my answer on Why is no string output with ‘echo %var%’ after using ‘set var = text’ on command line? It explains in full details why the recommended syntax set "variable=value"
and not set variable="value"
is used with first double quote character left to the variable name instead of beginning of string value assigned to the environment variable. This change requires referencing the environment variables with a URL or file name with enclosing the variable reference in "
for always working independent on the string values assigned to the environment variables.
The subdirectory pdfs
is created in the directory of the batch file in case of not existing. There is no additional check for testing if the subdirectory creation was successful. The PDF file download fails on missing subdirectory pdfs
could not be created which results in an output error message.
The evaluation of the exit code of CURL is done using the recommend syntax as described by the usage help of command IF. The exit code evaluation of a command or executable is done faster and works always on using this syntax.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
call /?
(explains%~dp0
which is drive and path of argument 0 which is the full batch file directory path)cmd /?
curl --help
(Linux instead of Windows syntax)echo /?
endlocal /?
for /?
goto /?
if /?
md /?
rem /?
robocopy /?
set /?
setlocal /?
The scheduled task should be configured with:
- Program/script:
%SystemRoot%System32cmd.exe
- Arguments:
/D /C "C:DropboxDocumentsProjectsProgrammingnyt_dailyBatchFileName.cmd"
- Start in:
C:DropboxDocumentsProjectsProgrammingnyt_daily
The Start in property could be also empty as the batch file does no longer depend on which directory is defined by the process starting cmd.exe
for processing the specified batch file as current directory.