AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Wildcard Folders
Goto page 1, 2  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Wish List
View previous topic :: View next topic  
Author Message
xx3nvyxx



Joined: 05 Sep 2005
Posts: 86
Location: Down the hall, on your left.

PostPosted: Thu Mar 06, 2008 3:41 am    Post subject: Wildcard Folders Reply with quote

I would like to be able to do this:

Code:
loop, %A_AppData%\.purple\logs\aim\*\randomsn\*.html
{
   Filelist := FileList "," A_loopFileName
}


The function would be to allow one folder (or multiple folders, but only if seperated by a \) to be variable and still be able to build a file list.

If you want a can build a "proof of concept" function, but I really would like to see it added to ahk itself.

Edit: Built the "proof of concept" function. I will keep this post up to date with the newest version, just in case it breaks or I add new features.


Version 1.1
Code:
;
; AutoHotkey Version: 1.0.47.5
; Language:       English
; Platform:       WinXP Pro
; Author:         William Washco <xx3nvyxx@gmail.com>
;
; Script Function:
;   Convert paths with wildcards to a file list.
;
/*
#singleinstance, force
WildcardFolder = C:\Program Files\*\*\
File = *.exe
List := WildcardFolderList(WildcardFolder,File)
msgbox, %List%
Return
*/
WildcardFolderList(Path,FilePattern)
{
   StringReplace, Newpath, Path, *\, ¤,A
   StringSplit, Folders, Newpath, ¤
   Loops := Folders0 - 1
   Folders1 := Folders1 . "*"
   Loop, %Loops%
   {
      Nextlevel := ""
      TempList := ""
      Wildcard := ""
      loop, parse, Folders%A_Index%, `,
      {
         loop, %A_LoopField%, 2
         {
            If (NextLevel != "")
               NextLevel := NextLevel . "," . A_LoopFileLongPath
            Else
               NextLevel := A_LoopFileLongPath
         }
      }
      Next := A_Index + 1
      If (A_Index != Loops)
         Wildcard := "*"
      Loop, parse, NextLevel, `,
      {
         If (A_Index != 1)
            TempList := TempList . "," A_LoopField . "\" . Folders%Next% . Wildcard
         Else
            TempList := A_LoopField . "\" . Folders%Next% . Wildcard
      }
      Folders%Next% := TempList
   }
   FoundFolders := 0
   loop, parse, TempList, `,
   {
      Ifexist, %A_LoopField%
      {
         If (FolderListing != "")
            FolderListing := FolderListing . "," . A_LoopField
         Else
            FolderListing := A_LoopField
      }
   }
   Loop, parse, FolderListing, `,
   {
      WildFile := A_LoopField . "\" . FilePattern
      TempFileList := ""
      Loop, %WildFile%
      {
         If (TempFileList != "")
            TempFileList := TempFileList . "," . A_LoopFileLongPath
         Else
            TempFileList := A_LoopFileLongPath
      }
      If (TempFileList = "")
         continue
      If (A_Index != 1)
         FileList := FileList . "," . TempFileList
      Else
         FileList := TempFileList
   }
   Return, FileList
}


*NOTE* My code posted here has been pointed out to NOT be the best solution. Please read further for code as short as 9 lines long. Including the function header and curly braces.
_________________
Now the world has gone to bed,
Darkness won't engulf my head,
I can see by infra-red,
How I hate the night.

Now I lay me down to sleep,
Try to count electric sheep,
Sweet dream wishes you can keep,
How I hate the night.


Last edited by xx3nvyxx on Sat Mar 08, 2008 5:44 pm; edited 3 times in total
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
LBJ



Joined: 03 May 2007
Posts: 16

PostPosted: Thu Mar 06, 2008 6:43 am    Post subject: Reply with quote

I absolutely agree that the *nix style of wildcard globbing is a truly elegant tool when used with certain file structures.

Web servers and mail servers in particular are very easily controlled with a simple glob like...

/home/*/public_html/*/config.php

...to pick up...

/home/fred/public_html/mail-app/config.php
/home/mary/public_html/mail-app/config.php
/home/mary/public_html/another-app/config.php

It would definitely be nice if the standard loop function provided extended *nix globbing rather than have to code a function wrapper to emulate it.
Back to top
View user's profile Send private message
xx3nvyxx



Joined: 05 Sep 2005
Posts: 86
Location: Down the hall, on your left.

PostPosted: Thu Mar 06, 2008 7:03 am    Post subject: Reply with quote

Speaking of a wrapper, I am nearing the completion of my (very messy) function to do just that. I hope that, should it not be implemented into the next version of AHK, it is at least cleaned up by someone more talented than myself. Razz

More news when I'm done.
Don't bother me, I'm coding.
_________________
Now the world has gone to bed,
Darkness won't engulf my head,
I can see by infra-red,
How I hate the night.

Now I lay me down to sleep,
Try to count electric sheep,
Sweet dream wishes you can keep,
How I hate the night.
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
xx3nvyxx



Joined: 05 Sep 2005
Posts: 86
Location: Down the hall, on your left.

PostPosted: Thu Mar 06, 2008 8:32 am    Post subject: Reply with quote

Code:
#singleinstance, force
WildcardFolder = %A_AppData%\.purple\logs\*\*\randomsn\
File = *.html
List := WildcardFolderList(WildcardFolder,File)
msgbox, %List%
Return

WildcardFolderList(Path,FilePattern)
{
   StringReplace, Newpath, Path, *\, ¤,A
   StringSplit, Folders, Newpath, ¤
   Loops := Folders0 - 1
   Folders1 := Folders1 . "*"
   Loop, %Loops%
   {
      Nextlevel := ""
      TempList := ""
      Wildcard := ""
      loop, parse, Folders%A_Index%, `,
      {
         loop, %A_LoopField%, 2
         {
            If (NextLevel != "")
               NextLevel := NextLevel . "," . A_LoopFileLongPath
            Else
               NextLevel := A_LoopFileLongPath
         }
      }
      Next := A_Index + 1
      If (A_Index != Loops)
         Wildcard := "*"
      Loop, parse, NextLevel, `,
      {
         If (A_Index != 1)
            TempList := TempList . "," A_LoopField . "\" . Folders%Next% . Wildcard
         Else
            TempList := A_LoopField . "\" . Folders%Next% . Wildcard
      }
      Folders%Next% := TempList
   }
   FoundFolders := 0
   loop, parse, TempList, `,
   {
      Ifexist, %A_LoopField%
      {
         FoundFolders += 1
         If (FoundFolders != 1)
            FolderListing := FolderListing . "," . A_LoopField
         Else
            FolderListing := A_LoopField
      }
   }
   Loop, parse, FolderListing, `,
   {
      WildFile := A_LoopField . "\" . FilePattern
      Loop, %WildFile%
      {
         If (A_Index != 1)
            TempFileList := TempFileList . "," . A_LoopFileLongPath
         Else
            TempFileList := A_LoopFileLongPath
      }
      If (A_Index != 1)
         FileList := FileList . "," . TempFileList
      Else
         FileList := TempFileList
   }
   Return, FileList
}


Here it is: V1.0
Note the path and file name are seprate. Also note the (not required) trailing backslash on the path. If you make a habit of including it it will be easier to remember the (required) trailing backslash if the path ends in a wildcard.
_________________
Now the world has gone to bed,
Darkness won't engulf my head,
I can see by infra-red,
How I hate the night.

Now I lay me down to sleep,
Try to count electric sheep,
Sweet dream wishes you can keep,
How I hate the night.
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
xx3nvyxx



Joined: 05 Sep 2005
Posts: 86
Location: Down the hall, on your left.

PostPosted: Thu Mar 06, 2008 6:40 pm    Post subject: Reply with quote

Code:
;
; AutoHotkey Version: 1.0.47.5
; Language:       English
; Platform:       WinXP Pro
; Author:         William Washco <xx3nvyxx@gmail.com>
;
; Script Function:
;   Convert paths with wildcards to a file list.
;
/*
#singleinstance, force
WildcardFolder = C:\Program Files\*\*\
File = *.exe
List := WildcardFolderList(WildcardFolder,File)
msgbox, %List%
Return
*/
WildcardFolderList(Path,FilePattern)
{
   StringReplace, Newpath, Path, *\, ¤,A
   StringSplit, Folders, Newpath, ¤
   Loops := Folders0 - 1
   Folders1 := Folders1 . "*"
   Loop, %Loops%
   {
      Nextlevel := ""
      TempList := ""
      Wildcard := ""
      loop, parse, Folders%A_Index%, `,
      {
         loop, %A_LoopField%, 2
         {
            If (NextLevel != "")
               NextLevel := NextLevel . "," . A_LoopFileLongPath
            Else
               NextLevel := A_LoopFileLongPath
         }
      }
      Next := A_Index + 1
      If (A_Index != Loops)
         Wildcard := "*"
      Loop, parse, NextLevel, `,
      {
         If (A_Index != 1)
            TempList := TempList . "," A_LoopField . "\" . Folders%Next% . Wildcard
         Else
            TempList := A_LoopField . "\" . Folders%Next% . Wildcard
      }
      Folders%Next% := TempList
   }
   FoundFolders := 0
   loop, parse, TempList, `,
   {
      Ifexist, %A_LoopField%
      {
         If (FolderListing != "")
            FolderListing := FolderListing . "," . A_LoopField
         Else
            FolderListing := A_LoopField
      }
   }
   Loop, parse, FolderListing, `,
   {
      WildFile := A_LoopField . "\" . FilePattern
      TempFileList := ""
      Loop, %WildFile%
      {
         If (TempFileList != "")
            TempFileList := TempFileList . "," . A_LoopFileLongPath
         Else
            TempFileList := A_LoopFileLongPath
      }
      If (TempFileList = "")
         continue
      If (A_Index != 1)
         FileList := FileList . "," . TempFileList
      Else
         FileList := TempFileList
   }
   Return, FileList
}


V1.1
-Fixed a bug which caused the function to return the same file "a number" of times if it could not find FilePattern in "a number" of folders.
-Changed the default path to something most people will get results on.
-The test case is commented out to make including this function easier.
_________________
Now the world has gone to bed,
Darkness won't engulf my head,
I can see by infra-red,
How I hate the night.

Now I lay me down to sleep,
Try to count electric sheep,
Sweet dream wishes you can keep,
How I hate the night.
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Fri Mar 07, 2008 2:02 am    Post subject: Reply with quote

Why not simply...
Code:
Loop, %A_AppData%\.purple\logs\aim\*, 2
{
    Loop, %A_LoopFileLongPath%\randommsn\*.html
    {
        ;...
    }
}
Question
Back to top
View user's profile Send private message
xx3nvyxx



Joined: 05 Sep 2005
Posts: 86
Location: Down the hall, on your left.

PostPosted: Fri Mar 07, 2008 2:15 am    Post subject: Reply with quote

That can't exactly be used for multiple applications. My function allows for any number of wildcard folders.
_________________
Now the world has gone to bed,
Darkness won't engulf my head,
I can see by infra-red,
How I hate the night.

Now I lay me down to sleep,
Try to count electric sheep,
Sweet dream wishes you can keep,
How I hate the night.
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
LBJ



Joined: 03 May 2007
Posts: 16

PostPosted: Sat Mar 08, 2008 7:27 am    Post subject: Reply with quote

lexiKos wrote:
Why not simply...
Code:
Loop, %A_AppData%\.purple\logs\aim\*, 2
{
    Loop, %A_LoopFileLongPath%\randommsn\*.html
    {
        ;...
    }
}
Question


Until you've used unix style globbing, it's hard to appreciate the benefit of it, or even to immediately see the difference.

However, once you've used it, it's horrible to work without it.

The code stub you've provided above relies on the coder or application providing the full path to the ultimate folder where the *.html files are to be found. That's fairly trivial compared to what unix globbing provides.

Imagine a web server with the following structure...

/home (or c:\home for Window/Dos syntax)

Under the /home folder are all private folders for all users. Let's work with tom, mary, sue, sally and bob, so...

/home/tom
/home/mary
/home/sue
/home/sally
/home/bob

Under each of those private folders is their root web folder, usually called public_html, so...

/home/tom/public_html
/home/mary/public_html
/home/sue/public_html
/home/sally/public_html
/home/bob/public_html

We could carry that on to a great depth, but let's now assume we want to delete all *.html files in each of those public_html folders, and *ONLY* in those public_html folders, so...

rm (or del for Windows) /home/*/public_html/*.html

That's totally different to standard Windows globbing as provided via the Loop, FilePattern routine of AHK.

As one more example, imagine we only wanted to delete the .pl files owned by sue and sally and only if they're in a folder under the public_html folder and only for folders with a g in the second position like cgi-bin, so...

rm /home/s*/public_html/?g*/*.pl

We run mostly *nix servers, but we also manage a lot of windows boxes, so one of the first things we code for any language we're using away from *nix is a globbing extension.

Our globbing for AHK is part of a larger routine we use for the admin of our email servers, but I've massaged out the basic routines and created a standalone function. It handles all standard globs using * and ? and accepts an optional secondary parameter to specify how to handle folders found at the final level similar to AHK's Loop, Filepattern, IncludeFolders syntax.

Relative and absolute path referencing are supported.

Typical calls could be of the form...

List := UnixGlob("C:\Program Files\*\o??ic*\*.exe")

...or to include/require folders at the final level...

List := UnixGlob("C:\Program Files\*\o??ic*\*.exe", [1/2])

Edit: For a truly elegant coding solution, see the post from lexiKos below...


Last edited by LBJ on Sun Mar 09, 2008 12:33 am; edited 2 times in total
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Sat Mar 08, 2008 1:01 pm    Post subject: Reply with quote

Perhaps you did not notice the inner loop in my example operates on each folder iterated by the outer loop. It is a trivial, functional equivalant of:
Code:
loop, %A_AppData%\.purple\logs\aim\*\randomsn\*.html
The same could be done for your example, with a loop for /home/*, and an inner loop for /home/whatever/public_html.
Quote:
That's totally different to standard Windows globbing as provided via the Loop, FilePattern routine of AHK.
It is merely a convenience; the loop or command is recursing for each wildcard, instead of requiring the programmer to do so explicitly.
Back to top
View user's profile Send private message
LBJ



Joined: 03 May 2007
Posts: 16

PostPosted: Sat Mar 08, 2008 1:19 pm    Post subject: Reply with quote

lexiKos wrote:
It is merely a convenience; the loop or command is recursing for each wildcard, instead of requiring the programmer to do so explicitly.


Yes, I follow what you're saying and what you coded, but you basically made the case for globbing with your last comment.

Obviously a solution can be custom coded for any situation, but *nix globbing makes it a quick one liner rather than a potentially deep nesting of directory parsing loops.

It's just another tool, albeit a very powerful one.
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Sat Mar 08, 2008 1:55 pm    Post subject: Reply with quote

What I didn't say is it's only of benefit if you specifically have a use for it. I don't. Thinking about it, though, I can understand how much better it would be for someone that has a need for it.

I think I mainly posted because xx3nvyxx's solution seemed overcomplicated, as does yours. Here is my crack at it:
Code:
Glob(list, "C:\Program Files\?u*y\*\*.exe")
MsgBox %list%

Glob(ByRef list, Pattern, IncludeDirs=0)
{
    if (i:=RegExMatch(Pattern,"[*?]")) && (i:=InStr(Pattern,"\",1,i+1))
        Loop, % SubStr(Pattern, 1, i-1), 2
            Glob(list, A_LoopFileLongPath . SubStr(Pattern,i), IncludeDirs)
    else
        Loop, %Pattern%, %IncludeDirs%
            list .= (list="" ? "" : "`n") . A_LoopFileLongPath
}
It seems too simple... maybe I missed something? Laughing
Back to top
View user's profile Send private message
xx3nvyxx



Joined: 05 Sep 2005
Posts: 86
Location: Down the hall, on your left.

PostPosted: Sat Mar 08, 2008 5:37 pm    Post subject: Reply with quote

...So how short is this function going to get?

I think everyone here has already completely shown me up. Which is, don't get me wrong, a very good thing. But I still would like to see this functionality added to ahk itself...
_________________
Now the world has gone to bed,
Darkness won't engulf my head,
I can see by infra-red,
How I hate the night.

Now I lay me down to sleep,
Try to count electric sheep,
Sweet dream wishes you can keep,
How I hate the night.
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
LBJ



Joined: 03 May 2007
Posts: 16

PostPosted: Sun Mar 09, 2008 12:31 am    Post subject: Reply with quote

lexiKos wrote:
It seems too simple... maybe I missed something? Laughing


Not at all! That's a perfectly elegant solution.

lexiKos wrote:
What I didn't say is it's only of benefit if you specifically have a use for it. I don't. Thinking about it, though, I can understand how much better it would be for someone that has a need for it.


Yes, as I said, it's just another useful tool, but it can be *very* useful.
Back to top
View user's profile Send private message
LBJ



Joined: 03 May 2007
Posts: 16

PostPosted: Tue Mar 11, 2008 4:21 am    Post subject: Reply with quote

xx3nvyxx wrote:
...So how short is this function going to get?


I doubt it will get much shorter, but building on the excellent solution of lexiKos, the line...

if (i:=RegExMatch(Pattern,"[*?]")) && (i:=InStr(Pattern,"\",1,i+1))

...can be simplified to a single RegExMatch as per...

if (i:=RegExMatch(Pattern,"(?<=[*?])\\"))

...giving...

Code:
Glob(list, "C:\Program Files\*\*\*.exe")
MsgBox %list%

Glob(ByRef list, Pattern, IncludeDirs=0)
{
    if (i:=RegExMatch(Pattern,"(?<=[*?])\\"))
        Loop, % SubStr(Pattern, 1, i-1), 2
            Glob(list, A_LoopFileLongPath . SubStr(Pattern,i), IncludeDirs)
    else
        Loop, %Pattern%, %IncludeDirs%
            list .= (list="" ? "" : "`n") . A_LoopFileLongPath
}




xx3nvyxx wrote:
But I still would like to see this functionality added to ahk itself...



Yes, I definitely agree. I can't see a downside to its inclusion.
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Tue Mar 11, 2008 6:34 am    Post subject: Reply with quote

LBJ wrote:
if (i:=RegExMatch(Pattern,"(?<=[*?])\\"))
That would allow wildcards only at the end of the name. Notice the pattern I used in my test: ?u*y.

Since look-behinds do not support quantifiers of varying size, the only way for it to work with a single regular expression would be to capture the string from [*?] to \\, and use an output variable to determine the position of the \.

Both methods perform roughly the same, but I see the InStr method as more intuitive. It also avoids an unnecessary variable...
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Wish List All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group