0

This is really driving me insane. The following is part of an 8,000 line batch script. This section of code is at line 3380 approx and not working for me, as the fext variable is not getting set.

Apologies for the long post.

Rem *********************************************************************************************************************************************   
Rem * Copy and register fonts                                                                                                                   *
Rem *********************************************************************************************************************************************   
:FONTS
    IF EXIST "%SrcPath%\Fonts\" (
        Echo Installing Custom Fonts
        Echo. >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
        Echo %time% === Installing Custom Fonts === >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
        FOR %%a in ("%SrcPath%\Fonts\*.?tf") do call :Installfont "%%~nxa"
        Echo %time% Fonts will not be available until the system has been restarted >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
        Echo %time% === Finished Installing Custom Fonts === >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
    )
    Goto SYMLNK

:Installfont
    Set font=
    Set fext=
    set ftype=
    Set "font=%~1"
    IF EXIST "%LOCALAPPDATA%\Microsoft\Windows\Fonts\%font%" echo Font "%font%" already installed. >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
    IF EXIST "%WINDIR%\Fonts\%font%" echo Font "%font%" already installed. >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
    IF NOT EXIST "%LOCALAPPDATA%\Microsoft\Windows\Fonts\%font%" IF NOT EXIST "%WINDIR%\Fonts\%font%" (
        Set "fext=%font:~-3%"
        IF "%fext%"=="" Echo null extension for font "%font%"
        IF /i "%fext%"=="ttf" Set ftype=Truetype
        IF /i "%fext%"=="otf" Set ftype=Opentype
        Echo %time% Installing "%font%" to "%LOCALAPPDATA%\Microsoft\Windows\Fonts" >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
        Echo %time% Processing font "%font%" Extension: %fext% Type: "(%ftype%)" >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
        echo "%font%" "%fext%" "%ftype%"
        Pause
        copy "%SrcPath%\Fonts\%font%" "%LOCALAPPDATA%\Microsoft\Windows\Fonts" /y >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
        REG ADD "HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Fonts" /v "%font% (%ftype%)" /d "%LOCALAPPDATA%\Microsoft\Windows\Fonts\%font%" /t REG_SZ /f >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
    )
    exit /b

:SYMLNK
    Echo Finished
    Endlocal

The srcpath and src variables are set at the top of the script...

@Echo Off
powershell -command "&{$w=(get-host).ui.rawui;$w.buffersize=@{width=132;height=2500};$w.windowsize=@{width=128;height=70};}"

setlocal

Rem What drive are we on? Save the source drive
    SET SRC=%CD:~0,2%
    
Rem Shorten the source path
    set SrcPath=%SRC%\PostInstall\%USERNAME%

If I don't clear the variables at the start of the subroutine the effect is cumulative. First time neither fext nor ftype are set. 2nd time fext is set, but ftype is not, third time both are set, so I added the sets at the start of the subroutine to clear them.

What I end up with is the registry entry name (eg). airstrike.ttf (). The value data is correct. What should be in the brackets is either truetype or opentype.

The copy command doesn't work at all. Just says 0 files copied.

Some fonts have spaces in the name such as Spirit & Ghost.ttf, which is why I've quoted everything.

I must admit I don't really have a good grasp of delayedexpansion but attempting to use it totally bricks the script.

The entire script contains a considerable amount of personal info, so sharing it is problematic. But if it's definitely needed I can go through and mask out the personal stuff, but then it's not an accurate copy of the real script.

You will see that the script writes to a log file. This is what's in the log file...

16:11:41.91 === Installing Custom Fonts === 
16:11:41.94 Installing "airstrike.ttf" to "C:\Users\Phillip\AppData\Local\Microsoft\Windows\Fonts" 
16:11:41.94 Processing font "airstrike.ttf" Extension:  Type: "()" 
        0 file(s) copied.
The operation completed successfully.

Would greatly appreciate some help with why this doesn't work.

thank you.

  • `Set "fext=%font:~-3%"` See [windows batch SET inside IF not working](https://stackoverflow.com/questions/9102422/windows-batch-set-inside-if-not-working). – dxiv Sep 29 '20 at 07:04
  • Thanks. If I use delayedexpansion it bricks the entire script. But that aside, I don't understand how to apply this to my problem. I removed all the echo statements and just left the assignments but the result is the same. – Citizen1138x Sep 29 '20 at 10:01
  • That's explained in the link, and now in the answer below as well. If it's still not clear, turn `echo on` and look at the `if` block that gets executed. Note that it's not about `if` in particular, the same applies to any `(...)` parenthesized block used in batch, such as `for-do(...)` loops etc. – dxiv Sep 29 '20 at 15:58

1 Answers1

1
IF EXIST "%LOCALAPPDATA%\Microsoft\Windows\Fonts\%font%" exit /b
IF EXIST "%WINDIR%\Fonts\%font%" exit /b
Set "fext=%font:~-3%"
IF "%fext%"=="" Echo null extension for font "%font%"
IF /i "%fext%"=="ttf" Set ftype=Truetype
IF /i "%fext%"=="otf" Set ftype=Opentype
Echo %time% Installing "%font%" to "%LOCALAPPDATA%\Microsoft\Windows\Fonts" >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
Echo %time% Processing font "%font%" Extension: %fext% Type: "(%ftype%)" >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
echo "%font%" "%fext%" "%ftype%"
Pause
copy "%SrcPath%\Fonts\%font%" "%LOCALAPPDATA%\Microsoft\Windows\Fonts" /y >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
REG ADD "HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Fonts" /v "%font% (%ftype%)" /d "%LOCALAPPDATA%\Microsoft\Windows\Fonts\%font%" /t REG_SZ /f >>C:\%USERDOMAIN%.PostInstall.Log 2>&1
exit /b

The issue is that within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).

Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.

Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion (outside of the block or more usually after the initial @echo off) and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.

Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.

In this case, simply exit /b if the block in question is NOT to be executed, then execute the individual statements of the block, so they will be processed as you expect, avoiding the characteristic of blocks - which is a commonly-encountered phenomenon with thousands of mentions in SO solutions.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • Thanks all. I removed all echos. I added delayed expansion after :fonts and endlocal before Goto Symlink, changed all % to ! and it worked better, but still wont copy the file and bricks other parts of the script. Adding delayedexpansion at the start of the script causes the script to crash immediately without doing anything. I'll try the above solution in the morning. – Citizen1138x Sep 30 '20 at 13:22
  • The copy was failing because the target folder didn't exist. The modified code above with the exit /b solved the problem. Thank you all. – Citizen1138x Oct 05 '20 at 05:12