Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

In the following code, I need to take the path and size of folder and subfolders into a file. But when the loop runs for the second time, path and size are not getting printed to the file. size.txt only contains the path and size of the 1st folder. Please somebody help me.

@echo off

SETLOCAL EnableDelayedExpansion
SET xsummary=
SET xsize=

for /f "tokens=1,2 delims=C" %%i IN ('"dir /s /-c /a | find "Directory""') do (echo    C%%j >> abcd.txt)
for /f "tokens=*" %%q IN (abcd.txt) do (
cd "%%q"
For /F "tokens=*" %%h IN ('"dir /s /-c /a | find "bytes" | find /v "free""') do Set    xsummary=%%h
For /f "tokens=1,2 delims=)" %%a in ("!xsummary!") do set xsize=%%b
Set xsize=!xsize:bytes=!
Set xsize=!xsize: =!
echo.%%q >> size.txt
echo.!xsize! >> size.txt

)

share|improve this question
add comment

2 Answers

up vote 1 down vote accepted

The problem is the cd — you are creating a two-line size.txt file in each directory.  Try

for ... %%h in ('"dir /s /-c /a  %%q  | find "bytes" | find /v "free""') ...

and remove the cd.

By the way, do you realize that you are getting cumulative totals?  For example, if your directory structure looks like

a   100K
a\b 200K
a\c 400K

then your result will say

a
700000
a\b
200000
a\c
400000

That’s because the second dir has the /s option.  That’s fine if that’s what you want; I just thought it might have been an oversight.

share|improve this answer
    
Thanks Scott. I forgot about that "cd" cammand. Your method worked with a little change. I included as "%%q" to include the folders whose names has spaces. And Yes I need the cumulative result only. Thanks for your concern. –  Sachin Oct 10 '12 at 18:13
    
@Sachin: And thanks for reminding me about quotes. I’m a Unix person first, and I quote variables in shell scripts reflexively, but I keep forgetting to do it in DOS. Um, I mean the Windows command line. –  Scott Oct 10 '12 at 20:50
add comment

If you can manage it, avoid this mess by grabbing the Sysinternals du utility and running du -v <directoryname>. Output will be kilobytes. The -v option means output the size of all subdirectories.


With your current script, the problem seems to be appending text while in a loop. An easy workaround is to output to the command prompt window, like so:

echo %%q
echo !xsize!

and when you run the batch file, sent its output to a text file: test.bat > size.txt


A quick tip: the first bit can be simplified with:

for /f "tokens=*" %%q in ('dir /a:d /s /b') do (
    cd "%%q"
    ...
    echo !xsize!
)

Basically, it's replacing the use of find with the /a:d option (list directories only) and /b (use bare format, no heading or summary). This also gets rid of the temporary file.


Modified version to work on folders with exclamation marks in the name. Using Scott's answer. See comments below.

Note that REM means it's a comment.

@echo off

REM clear the file
type NUL > size.txt

for /f "tokens=*" %%q IN ('dir /a:d /s /b') do (
    echo %%q >> size.txt
    REM Run :getsize, passing "%%q" including quotes as the first parameter
    call :getsize "%%q" >> size.txt
)

REM go to the end of the file, i.e. exit
goto :eof

:getsize
REM %1 is the  first parameter. It should already be quoted.
for /f "tokens=*" %%h IN ('"dir /s /-c /a %1 | find "bytes" | find /v "free""') do set xsummary=%%h
for /f "tokens=1,2 delims=)" %%a in ("%xsummary%") do set xsize=%%b
set xsize=%xsize:bytes=%
set xsize=%xsize: =%
echo %xsize%

goto :eof

An alternative is to use PowerShell:

Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | %{$_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0; $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum; $_} | Sort-Object DirSize -Descending | Select-Object -First 10 FullName,DirSize | Format-Table -AutoSize | Out-String -Width 4096 | Out-File 'sizes.txt';

This is overly complex for the purpose of being as complete as possible - also, most of those commands have shorter aliases; this is just more readable. And if you separate certain sections out into functions/their own line, it becomes much more readable and possibly shorter.

Expanding it:

  • Get-ChildItem -Recurse

    Recursively list all subdirectories and files

  • ?{ $_.PSIsContainer }

    Filter so only directories are kept (? is an alias for Where-Object). $_ refers to the object(s) piped to this - since Get-ChildItem passes a list/array of objects, this processes them one at a time.

  • %{$_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0; $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum; $_}

    Calculate size of each directory (% is an alias for Foreach-Object, so run what is between { } on each individual object. Each object is a directory, as per the previous parts.)

    Expanding it:

    • $_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0;

      A semicolon (;) denotes the end of a line/statement, and does not pass the output on, as opposed to a pipe (|). This particular bit has no output, and it's just adding the DirSize property to the directory object and initialising it to 0. This is done for easier filtering and neater output later.

    • $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum;

      Set DirSize to the Sum property of the output of that command. $() means execute what is inside the parentheses and substitute that in.

      Expanding it:

      • Get-ChildItem -Recurse $_.Fullname

        Get all the contents of the current directory object ($_, Fullname just means the full path). -Recurse means count the contents of subdirectories as well.

      • ?{ -not $_.PSIsContainer }

        Filter so only files are kept. Directories have a Length property of 0, so we need to use sum the files within - essentially what your batch script does, except we're dealing with actual numbers, objects and properties instead of grabbing a 'number' as text using find.

      • Measure-Object -Sum -Property Length

        Calculate the sum of all the Length properties passed to it. This returns an object containing the property Sum, which is used above (.Sum)

    • $_

    Essentially echoing the object to pass it on to the next section. It now has a DirSize property.

  • Sort-Object DirSize -Descending

    Sort descending by DirSize

  • Select-Object -First 10 FullName,DirSize

    Only output the first 10, and only the name and size

  • Format-Table -AutoSize | Out-String -Width 4096 | Out-File 'sizes.txt';

    Output to sizes.txt. The Format-Table and Out-String bits are to prevent truncating to console window width (normally 80 characters). 4096 should be more than enough characters for any Windows path and directory size, considering a Windows path is typically limited to 255 characters (IIRC). The AutoSize just means make it as short as possible without truncating, instead of padding with a whole lot of whitespace to fit 4096 characters.

And the command as a one liner to run in cmd.exe (you can run the above version within PowerShell):

powershell -c "Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | %{$_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0; $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum; $_} | Sort-Object DirSize -Descending | Select-Object -First 10 FullName,DirSize | Format-Table -AutoSize | Out-String -Width 4096 | Out-File 'sizes.txt';"
share|improve this answer
    
Thanks for the quick tip. It really resolved a lot of complexities. –  Sachin Oct 10 '12 at 18:11
    
"dir /a:d /s /b" this cammand is removing special characters from the path of the directory. I need the exact path. Can u help me? –  Sachin Oct 11 '12 at 16:11
    
@Sachin What are those special characters? Could you provide an example path that this affects? –  Bob Oct 11 '12 at 16:26
    
original path: D:\Songs\Audio\Hindi\Wake Up S!d and it changed to D:\Songs\Audio\Hindi\Wake Up Sd –  Sachin Oct 11 '12 at 16:30
    
@Sachin This problem isn't due to the dir command - your original would do the same thing. The problem is because of EnableDelayedExpansion. cmd first parses a line in a batch file to figure out what it should do, and normally expands variables at this time (%variablename%). Then it executes the line. EnableDelayedExpansion means expand variables in exclamation marks (!variablename!) at execution time. What's happening here is the variable %%q is being expanded at parse time and then cmd is choking on the ! at execution time. –  Bob Oct 11 '12 at 16:45
show 12 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.