Search code examples
windowsbatch-filemklink

Listing non symbolic link on Windows


I'm trying to list non-symbolic links in an specific directory and then delete them.

non-symbolic links definition: any file besides the ones that have been created using the command MKLINK /H

To identify those non-symbolic links I used the following approach:

#> fsutil hardlink list %file% | find /c /v ""

When the specified %file% is a symbolic link it returns a number bigger than 1, when it is just a simple file, it returns 1. So far so good!

My problem starts when I need to automate this process and get such return to compare if it is bigger than 1

That's is the code I'm trying to get running property:

@echo off    
set some_dir=C:\TEMP\    
for /f %%a in ('dir /b %some_dir%') do (
    set count=fsutil hardlink list %%a | find /c /v ""
    if %count% -EQU 1 del /Q %%a        
)

Would someone explain me how such attribution and comparison on %count% variable could be done, please?


Solution

  • There are some problems in your code:

    • you need delayed expansion because you are setting (writing) and expanding (reading) the variable count within the same parenthesised block of code (namely the for /F %%a loop);
    • in your for /F %%a loop you need to state options "eol=| delims=" in order not to run into trouble with files whose names begin with ; (such would be ignored due to the default eol=; option) and those which have white-spaces in their names (you would receive only the postion before the first white-space because of the default delims SPACE and TAB and the default option tokens=1 (see for /? for details about that);
    • dir /B returns file names only, so %%a actually points to files in the current directory rather than to C:\TEMP\; to fix that, simply change to that directory first by cd;
    • to capture the output of a command (line) and assign it to a variable, use another for /F loop and set; this loop is going to iterate once only, because find /C returns only a single line; note the escaped pipe ^| below, which is required to not execute it immediately;
    • there is no comparison operator -EQU, you need to remove the - to check for equality;
    • it is a good idea to use the quoted set syntax as it is most robust against poisonous characters;
    • file and directory paths should generally be quoted since they might contain token delimiters or other poisonous characters;

    Here is the fixed script:

    @echo off
    setlocal EnableDelayedExpansion
    pushd "C:\TEMP\" || exit /B 1
    for /F "eol=| delims=" %%a in ('dir /B "."') do (
        for /F %%b in ('
            fsutil hardlink list "%%a" ^| find /C /V ""
        ') do (
            set "count=%%b"
        )
        if !count! EQU 1 del "%%a"
    )
    popd
    endlocal
    

    This can even be simplified:

    @echo off
    pushd "C:\TEMP\" || exit /B 1
    for /F "eol=| delims=" %%a in ('dir /B "."') do (
        for /F %%b in ('
            fsutil hardlink list "%%a" ^| find /C /V ""
        ') do (
            if %%b EQU 1 del "%%a"
        )
    )
    popd
    

    Since the inner for /F loop iterates always once only, we can move the if query inside, thus avoiding the definition of an auxiliary variable which is the only one we needed delayed expansion for.