none
How do I read multiple lines from a text file and insert each as the path for a series of multi-layered directories? RRS feed

  • Question

  • I'm trying to set up a VBS script that will set up multiple directories, each a number of folders deep.

    I can set up one multi-level directory using the following code:

    Option Explicit

    Dim objFSO


    Const PATH = "c:\THIS\IS\A\TEST"

    Set objfso = CreateObject("Scripting.FileSystemObject")

    BuildFullPath PATH

    Sub BuildFullPath(ByVal FullPath)
    If Not objfso.FolderExists(FullPath) Then
    BuildFullPath objfso.GetParentFolderName(FullPath)
    objfso.CreateFolder FullPath
    End If
    End Sub

    WScript.Quit

    And I set up a text file containing all the "PATHS" I want to create.

    I also have a DO UNTIL loop that reads each line of the text file. 

    Option Explicit

    Dim objFSO,objTextFile,strNextLine
    Const ForReading = 1
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFile = objFSO.OpenTextFile _
        ("c:\logs\UNITCASHDIR.txt", ForReading)
    Do Until objTextFile.AtEndOfStream

        strNextLine = objTextFile.Readline
        Wscript.Echo strNextLine
    Loop

    This code just shows what each line is.  I want to take that line and use it as my next "PATH" I'm assuming that I need to replace the constant "PATH" with a variable, so that I can update the line, so I used strPATH.  But what I can't figure out is how to take the line that is read and make it the variable.

    Here is what I have put together from the two sets of code above, but I get a syntax error when it gets to line 17:

    Option Explicit

    Dim objFSO,objTextFile,strNextLine,strPath

    Const ForReading = 1
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFile = objFSO.OpenTextFile _
        ("c:\logs\UNITCASHDIR.txt", ForReading)

    Do Until objTextFile.AtEndOfStream

        strNextLine = objTextFile.Readline
        strPath = strNextLine

    BuildFullPath strPath

    Sub BuildFullPath(ByVal FullPath)
    If Not objfso.FolderExists(FullPath) Then
    BuildFullPath objfso.GetParentFolderName(FullPath)
    objfso.CreateFolder FullPath
    End If
    End Sub

    Loop

    WScript.Quit

    What should strPATH really "="?? 

    Thanks in advance...

    Tuesday, June 25, 2013 8:16 PM

Answers

  • Now to make this complete I can show you how to fix your original attempt but file errors can still cause this to fail with useless error messages due to recursion.

    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFile = objFSO.OpenTextFile("c:\scripts\paths_to_build.txt")
    
    Do Until objTextFile.AtEndOfStream
    
    	BuildFullPath Trim(Replace(objTextFile.Readline(),"""",""))
    
    Loop
    
    Sub BuildFullPath(FullPath)
    	If Not objfso.FolderExists(FullPath) Then
    		BuildFullPath objfso.GetParentFolderName(FullPath)
    		WScript.Echo "CREATING:" & FullPath
    		objfso.CreateFolder FullPath
    	End If
    End Sub
    
    

    This version removes outer white space and any quotes around the strings.  Both of these will cause infinite recursion due to null paths.

    I removed all unnecessary code and added a message output so you can see what is happening and to make the code less confusing to read.  You should never use a line of code that you do not complete understand.  You could end up formatting your 'C' drive or losing critical data.


    ¯\_(ツ)_/¯

    • Marked as answer by KIICHLILA Wednesday, June 26, 2013 4:03 PM
    Wednesday, June 26, 2013 12:09 PM

All replies

  • You cannot have a Sub inside a Do loop. Why do you actually declare that small block of code a Sub? If you insist on making it a sub then you should put it at the end of the code.
    Tuesday, June 25, 2013 8:33 PM
  • I'm not sure what hapens if you put your subroutine's definition inside a loop like that;  I've never tried it.  Just to be on the safe side, rearrange the code like this.  Otherwise, it looks like you're on the right track.

    I don't like having the BuildFullPath sub use a global variable (objFSO) rather than creating its own, but it should technically work.

    Option Explicit
     
    Dim objFSO,objTextFile,strNextLine,strPath
     
    Const ForReading = 1
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFile = objFSO.OpenTextFile _
         ("c:\logs\UNITCASHDIR.txt", ForReading)
    
    Do Until objTextFile.AtEndOfStream
      strNextLine = objTextFile.Readline
      strPath = strNextLine
      BuildFullPath strPath 
    Loop
     
    WScript.Quit
    
    Sub BuildFullPath(ByVal FullPath)
      If Not objfso.FolderExists(FullPath) Then
        BuildFullPath objfso.GetParentFolderName(FullPath)
        objfso.CreateFolder FullPath
      End If
    End Sub
    

    Tuesday, June 25, 2013 8:35 PM
  • I'm not sure what hapens if you put your subroutine's definition inside a loop like that;  I've never tried it.  Just to be on the safe side, rearrange the code like this.  Otherwise, it looks like you're on the right track.

    What happens is thing go "BUMP!"  in the code and all manner of funny things happen.

    Actually it is allowed however the function is local to the loop that it is in and to the sub or module that it might be in.

    It is a bad practice unless you know exactly what you are doing.

    Why do people just blindly paste things then holler for help. This is an excellent time to learn how it work with a little trial end error.  TO many today just want an answer; no desire to know and lacking in curiosity.

    First off we need a good explanation of what is to happen.  Is this building a path from a list of names?  Are you trying to just add each one on the end of last one?  If so there is a very easy way to do it without functions or any fancy coding.


    ¯\_(ツ)_/¯

    Tuesday, June 25, 2013 11:10 PM
  • David - I don't think yours will work.  It does not seem to anticipate level at all.

    Of course this all depends on what is in the file.

    Assume we want to build this path:

    \root\sub\sub\sub\sub.

    Since we can't do it in one call (like Unix) we must do it in parts.

    Cat the parts and test - if not exist create - loop over path until done.

    parts = Split("\root\sub\sub\sub\sub","\")

    Now just walk the array

    I= 0
    fullpath = "\" & parts(I)
    loop and add to path while testing and creating each part.

    None of this requires more than a simple loop.


    ¯\_(ツ)_/¯

    Tuesday, June 25, 2013 11:15 PM
  • I didn't write that;  I just moved the Sub's definition out of the loop.  Note that BuildFullPath is a recursive subroutine which calls itself for the parent directory, which should work fine.

    I didn't actually test this code, but I use a very similar recursive function for this purpose in my own VBScripts, so I was fairly confident that it would work.

    Wednesday, June 26, 2013 12:12 AM
  • I don't see how it can work.  The idea is good but the implantation is faulty.  The test is only for one node and not for the full path even thought the variables called "FullPath".


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 12:33 AM
  • Here's the sub again:

    Sub BuildFullPath(ByVal FullPath)
      If Not objfso.FolderExists(FullPath) Then
        BuildFullPath objfso.GetParentFolderName(FullPath)
        objfso.CreateFolder FullPath
      End If
    End Sub

    Let's trace the execution for BuildFullPath "C:\Does\Not\Exist", assuming that none of the 3 subfolders of C: already exist.

    • BuildFullPath calls objFSO.FolderExists("C:\Does\Not\Exist"), gets False.
    • BuildFullPath calls BuildFullPath "C:\Does\Not\"
    •     BuildFullPath calls objFSO.FolderExists("C:\Does\Not"), gets False
    •     BuildFullPath calls BuildFullPath "C:\Does\"
    •         BuildFullPath calls objFSO.FolderExists("C:\Does"), gets False
    •         BuildFullPath calls BuildFullPath "C:\"
    •             BuildFullPath calls objFullPath.FolderExists("C:\"), gets True.
    •             BuildFullPath exits.
    •         BuildFullPath calls objFSO.CreateFolder "C:\Does"
    •         BuildFullPath exits.
    •     BuildFullPath calls objFSO.CreateFolder "C:\Does\Not"
    •     BuildFullPath exits.
    • BuildFullPath calls objFSO.CreateFolder "C:\Does\Not\Exist"
    • BuildFullPath exits.

    Looks good to me.  The necessary calls to CreateFolder are made, in the correct order, for any folder that doesn't already exist.

    Edit:  Here's my version of basically the same code that I've used for years.  The only real differences are that mine returns a True/False success value, and I use a RegEx to parse out the parent folder name.  (I don't remember why I did that;  probably because I'm a RegEx nut, and because I probably didn't know that the convenient FileSystemObject.GetParentFolderName method existed when I wrote it.)

    Function CreateFolderTree(strFolderName)
      ' Recursive routine to create a folder (including nonexistent parent folders, if needed)
      ' Returns True if folder structure is successfully created.
      
      Dim strParent
      Dim objFSO
      
      Set objFSO = CreateObject("Scripting.FileSystemObject")
      
      strParent = RegExGetBRef(strFolderName, "^(.+)\\[^\\]+\\?$", 1, True)
      
      If (Not objFSO.FolderExists(strParent)) Then
        If (Not CreateFolderTree(strParent)) Then
          CreateFolderTree = False
          Exit Function
        End If
      End If
      
      On Error Resume Next
      objFSO.CreateFolder strFolderName
      On Error Goto 0
      
      If (objFSO.FolderExists(strFolderName)) Then
        CreateFolderTree = True
      Else
        CreateFolderTree = False
      End If
      
      Set objFSO = Nothing
    End Function

    • Edited by David Wyatt Wednesday, June 26, 2013 12:46 AM added other function
    Wednesday, June 26, 2013 12:43 AM
  • I am very doubtful.  You will have to build it and run it.

    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 1:15 AM
  • Oh!  We still do not know what is in the file.

    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 1:15 AM
  • I am very doubtful.  You will have to build it and run it.

    ¯\_(ツ)_/¯


    I did just tell you that I've used that function for years.  I think I would have noticed by now if it didn't work, but be doubtful all you like.
    Wednesday, June 26, 2013 1:24 AM
  • I have no doubt about the function.  It is the rest of the script I doubt.  What is in the file?

    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 1:28 AM
  • My best guess is this is what you are trying to do.

    BuildFullPath "C:\scripts\test1\test2\test4"
    
    Sub BuildFullPath(FullPath)
         Set fso = CreateObject("Scripting.FileSystemObject")
         If Not fso.FolderExists(FullPath) Then
              BuildFullPath fso.GetParentFolderName(FullPath)
              fso.CreateFolder FullPath
         End If
    End Sub


    ¯\_(ツ)_/¯


    • Edited by jrv Wednesday, June 26, 2013 1:46 AM edit
    Wednesday, June 26, 2013 1:45 AM
  • Here is a sample of the text file I created that I want to pull in line by line into strPATH:

    "G:\Public\UnitCash 2013\AHC\2101"
    "G:\Public\UnitCash 2013\AHC\2102"
    "G:\Public\UnitCash 2013\AHC\2103"
    "G:\Public\UnitCash 2013\AHC\2104"
    "G:\Public\UnitCash 2013\AHC\2105"
    "G:\Public\UnitCash 2013\SOUTH\3101"
    "G:\Public\UnitCash 2013\SOUTH\3102"
    "G:\Public\UnitCash 2013\SOUTH\3103"
    "G:\Public\UnitCash 2013\NORTH\4101"
    "G:\Public\UnitCash 2013\NORTH\4102"
    "G:\Public\UnitCash 2013\NORTH\4103"

    So, I can create a full path for one of the lines by doing just the following:

    Option Explicit

    Dim objFSO


    Const PATH = ""G:\Public\UnitCash 2013\AHC\2101""

    Set objfso = CreateObject("Scripting.FileSystemObject")

    BuildFullPath PATH

    Sub BuildFullPath(ByVal FullPath)
    If Not objfso.FolderExists(FullPath) Then
    BuildFullPath objfso.GetParentFolderName(FullPath)
    objfso.CreateFolder FullPath
    End If
    End Sub

    WScript.Quit

    Rather than write sub after sub and inserting another full path in each constant, I was hoping to read each line into a variable within the loop.  So the second time through the loop would go to the next line in the file, and update strPATH with "G:\Public\UnitCash 2013\AHC\2102".  The BuildFullPath works, that isn't an issue.  I just need to know how once the Do Until Loop reads the next line, what the correct code is to pull that line in as the variable strPATH.

    Wednesday, June 26, 2013 5:49 AM
  • Why do you have so many quotes on everything?  This will cause you problems.

    Just read the file in a loop and feed each line to the function.

    While Not file.AtEndOfStream
           BuildFullPath file.ReadLine()
    Wend


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 10:59 AM
  • I posted what I thought you wanted you code to look like.  It still won't work even if you learn how to read a file and loop.

    You must decompose the path and build it in pieces.  It is the easiest way.


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 11:35 AM
  • Here is the easiest way to do this without using recursion or an other unnecessary and fragile mechanisms:

    Const PATH_FILE = "c:\scripts\paths_to_build.txt"
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set file = fso.OpenTextFile(PATH_FILE)
    
    While Not file.AtEndOfStream
    	BuildFullPath Trim(file.ReadLine())
    Wend
    
    Sub BuildFullPath(strPath)
         Set fso = CreateObject("Scripting.FileSystemObject")
         parts=Split(strPath,"\")
         path=parts(0)
         For i = 1 To UBound(parts)
            path = path & "\" & parts(i)
         	If Not fso.FolderExists(path) Then
            	WScript.Echo "Creating:" & path
         		fso.CreateFolder(path)
         	End If
         Next
    End Sub
    


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 11:47 AM
  • The reason the original code and Dave's code failed is because recursion does not work when there is ann issue with the file contents so you will get a out-of-memory error with no explanation.  If we do not use recursion or any other tricky methods then we will immediately see the errors caused by your file.

    I suspect you file is also Unicode and little-endian which cannot be read correctly by VBScript.  Be sure to save you file as ANSI and do not use quotes.


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 11:51 AM
  • Now to make this complete I can show you how to fix your original attempt but file errors can still cause this to fail with useless error messages due to recursion.

    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFile = objFSO.OpenTextFile("c:\scripts\paths_to_build.txt")
    
    Do Until objTextFile.AtEndOfStream
    
    	BuildFullPath Trim(Replace(objTextFile.Readline(),"""",""))
    
    Loop
    
    Sub BuildFullPath(FullPath)
    	If Not objfso.FolderExists(FullPath) Then
    		BuildFullPath objfso.GetParentFolderName(FullPath)
    		WScript.Echo "CREATING:" & FullPath
    		objfso.CreateFolder FullPath
    	End If
    End Sub
    
    

    This version removes outer white space and any quotes around the strings.  Both of these will cause infinite recursion due to null paths.

    I removed all unnecessary code and added a message output so you can see what is happening and to make the code less confusing to read.  You should never use a line of code that you do not complete understand.  You could end up formatting your 'C' drive or losing critical data.


    ¯\_(ツ)_/¯

    • Marked as answer by KIICHLILA Wednesday, June 26, 2013 4:03 PM
    Wednesday, June 26, 2013 12:09 PM
  • Jrv... dude.  Recursive functions are programming 101.  They're not any more "tricky" or "fragile" than writing a loop and making sure it's not infinite.  And again with the crappy attitude toward the people posting the questions.  "It still won't work even if you learn how to read a file and loop."  ?  Seriously?  Could you be any more condescending?

    Kiichlila, jrv does have a point that your code doesn't perform any type of validation on the path, and that this could cause some problems.  In this case, having quotation marks in the text file is going to cause problems.  You might also run into trouble if you tried to run this on a system where the G:\ drive didn't exist.  I haven't tested this, but it looks like your BuildFullPath sub might run forever if no part of the path (including the root) exists;  it'll eventually call itself with a blank string for the FullPath argument, and just keep going.

    There are more robust ways to fix this (like making sure you're not trying to create a root drive letter or share name, and adding some error handling / feedback when there are problems), but this is a simple way to at least avoid the infinite recursion:

    Sub BuildFullPath(ByVal FullPath)
      If (FullPath = "") Then Exit Sub
      If Not objfso.FolderExists(FullPath) Then
        BuildFullPath objfso.GetParentFolderName(FullPath)
        objfso.CreateFolder FullPath
      End If
    End Sub

    Wednesday, June 26, 2013 1:00 PM
  • It is fragile in VBScript because certain errors will not get trapped and the scrip will  loop until it is out of memory.  That is what happens with that recursive loop when there is a bad file.

    There are no recursive loops.  It is not a valid concept.  Recursion is an advanced programming concept and almost never taught in 101 classes although reference and a demo may be given.  If you remember that the 20x course in Advanced Programming Concepts always go into recursion in depth.  There are significant issues with the use of recursion such as error and stack control.  Recursion is not what is known as not finite or may not have a known end to its loop.  In compiled languages this can cause a stack overwrite and in scripts it cause an out-of-memory error. 

    This case the errors were because the paths had quotes which caused a file system error which could not be reported.  If we remove the recursion from the solution and use a simple loop then the error becomes immediately obvious and can be fixed.

    I am not saying your code is wrong it is just not necessary and will blow up if there is an error.

    Remember Occam's Razor.  It is your friend and your guide.  It will keep you out of trouble.


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 1:15 PM
  • Jrv... dude.  Recursive functions are programming 101.  They're not any more "tricky" or "fragile" than writing a loop and making sure it's not infinite.  And again with the crappy attitude toward the people posting the questions.  "It still won't work even if you learn how to read a file and loop."  ?  Seriously?  Could you be any more condescending?

    Kiichlila, jrv does have a point that your code doesn't perform any type of validation on the path, and that this could cause some problems.  In this case, having quotation marks in the text file is going to cause problems.  You might also run into trouble if you tried to run this on a system where the G:\ drive didn't exist.  I haven't tested this, but it looks like your BuildFullPath sub might run forever if no part of the path (including the root) exists;  it'll eventually call itself with a blank string for the FullPath argument, and just keep going.

    There are more robust ways to fix this (like making sure you're not trying to create a root drive letter or share name, and adding some error handling / feedback when there are problems), but this is a simple way to at least avoid the infinite recursion:

    Sub BuildFullPath(ByVal FullPath)
      If (FullPath = "") Then Exit Sub
      If Not objfso.FolderExists(FullPath) Then
        BuildFullPath objfso.GetParentFolderName(FullPath)
        objfso.CreateFolder FullPath
      End If
    End Sub

    This is unnecessary if you do not use recursion. The error will be obvious. There can be many more errors in the file that will not get trapped byu your approach and recursion will prevent them from being reported just like with the blank string.  The OS reports erros very nicely. Let it do its thing.  Recursion prevents many errors from being reported.

    In PowerShell we have SEH (Structured Exception Handling) which prevents the overwrite of errors due to recursion.  The error stack is always retained and reported.  On a stack overflow we can walk the stack back to the cause of the error and see wwhat was wrong in almost all cases.  This cannot be done in VBScript.


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 1:20 PM
  • You two can continue to duke it out if you like, but you did answer my question, thank you both.  I am obviously a novice to VBScript, and have been dually chastised for my lack of knowledge.  But that is the exact reason I reached out, rather than do something stupid and irreversible.

    Wednesday, June 26, 2013 4:03 PM
  • You two can continue to duke it out if you like, but you did answer my question, thank you both.  I am obviously a novice to VBScript, and have been dually chastised for my lack of knowledge.  But that is the exact reason I reached out, rather than do something stupid and irreversible.

    That is very good.

    You do need to start with the very first day basics so you can actually understand what it is you are pasting together.  An hours reading and you might have solved this by yourself and have the knowledge to solve future issues.

    Try this link: http://technet.microsoft.com/en-us/scriptcenter/dd772284


    ¯\_(ツ)_/¯

    Wednesday, June 26, 2013 4:24 PM