1

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?

  • 1
    You need [delayed expansion](http://ss64.com/nt/delayedexpansion.html) since you are setting *and* reading variable `count` in the same block of code; to assign the output of `fsutil` to a variable, use `for /F` just like you do to capture the output of `dir /B`; the `-` in front of `EQU` must be removed (typo?)... – aschipfl Aug 29 '16 at 19:26
  • @aschpfl: setlocal EnableDelayedExpansion added to script to handle reading and writing variable; -EQU fixed, this - came wrongly from bash way to compare :); but the part where you said to use FOR /F just I did one line up, I could understand. Do you want to me to iterate over fsutils command? Could you give a simple example of it, please? – VeryNiceArgumentException Aug 29 '16 at 20:07
  • 3
    Crossposted on SU (and answered there as well). [Listing non symbolic link on Windows](https://superuser.com/q/1118890). Please do **not** crosspost. See [Is cross-posting a question on multiple Stack Exchange sites permitted if the question is on-topic for each site?](https://meta.stackexchange.com/a/64069) – DavidPostill Aug 29 '16 at 22:11
  • @RaphaelMoita... Apologies, I'm not quite sure I understand the question :S Do you want to delete all links (i.e. hard, soft, shorcut) that are not symbolic belonging to the specified file(s) ...OR... all files that are not hard link files (i.e. created using mklink /h) belonging to a specified file in a specified directory... OR... do you want to delete files which have non-symbolic links associated with them :S ? – Albert F D Aug 30 '16 at 06:43
  • @Cris I wanna delete all files that have non-symbolic links associated with. – VeryNiceArgumentException Aug 30 '16 at 16:12

3 Answers3

2

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

There are some issues with your code.

for /f %%a in ('dir /b %some_dir%') do (

The dir /b doesn't work because it doesn't return a file name with a complete path (which is required as input for fsutil)

Use dir /b /s instead.

set count=fsutil hardlink list %%a | find /c /v ""

This doesn't set count to anything sensible.

Use another for /f and parse the output of fsutil so you can set a variable.

if %count% -EQU 1 del /Q %%a

This has two mistakes. You need to use delayed expansion to evaluate count correctly. Replace %count% with !count!. Also remove the -. Replace -EQU with EQU.

Try the following batch file.

test.cmd:

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

Notes:

  • Remove the echo when you happy with what the result will be.

Example usage and output:

I have used f:\test\folder1 as my test directory. hard is a hardlink to 1.txt.

F:\test>dir f:\test\folder1
 Volume in drive F is Expansion
 Volume Serial Number is 3656-BB63

 Directory of f:\test\folder1

29/08/2016  21:40    <DIR>          .
29/08/2016  21:40    <DIR>          ..
21/08/2016  09:46                 0 1.txt
21/08/2016  09:46                 0 2.txt
21/08/2016  09:46                 0 3.txt
21/08/2016  09:46                 0 4.txt
21/08/2016  09:46                 0 5.txt
29/08/2016  21:38    <SYMLINK>      file [f:\d]
21/08/2016  09:46                 0 hard
29/08/2016  21:38    <SYMLINKD>     test [f:\d]
               7 File(s)              0 bytes
               3 Dir(s)  1,764,846,960,640 bytes free

F:\test>test
del /Q f:\test\folder1\2.txt
del /Q f:\test\folder1\3.txt
del /Q f:\test\folder1\4.txt
del /Q f:\test\folder1\5.txt
del /Q f:\test\folder1\file
del /Q f:\test\folder1\test

Further Reading

DavidPostill
  • 7,734
  • 9
  • 41
  • 60
1

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.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
0

Simplified method:

@Echo Off
PushD X:\YourDirectory
For %%a In (*.*) Do (
    FSUtil HardLink List %%a|FindStr/VIC:"%%~pnxa">Nul||(Echo=Del "%%~a"))
Pause

When you're satisfied with the output Remove line 5 and also 'Echo=' from line 4

Compo
  • 36,585
  • 5
  • 27
  • 39
  • There is no `Delete` command in `cmd`... I'd not use `pushd` without `popd` to not fill the directory stack senselessly... – aschipfl Aug 30 '16 at 08:41
  • Thank you for your attention, the delete was a deliberate attempt to ensure that I received feedback from the OP, but I will adjust accordingly. The stack isn't unnecessarily filled with one path only and that stack is immediately cleared after the next line when the script closes. _Additionally there is no need to use the **/Q** parameter with Del as you have_. – Compo Aug 30 '16 at 13:46
  • The `pushd` directory stack is not cleared when a batch script terminates, it is still available in the owning `cmd` instance; but if the batch script is run by double-clicking, then you are right, the stack is cleared, of course. Yes, `/Q` does not make sense, thanks for pointing that out! – aschipfl Aug 30 '16 at 14:22