Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Wildcard Folders


  • Please log in to reply
20 replies to this topic
xx3nvyxx
  • Members
  • 93 posts
  • Last active: Jan 21 2016 09:33 AM
  • Joined: 05 Sep 2005
I would like to be able to do this:

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
;
; AutoHotkey Version: 1.0.47.5
; Language:       English
; Platform:       WinXP Pro
; Author:         William Washco <[email protected]>
;
; 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.

LBJ
  • Members
  • 24 posts
  • Last active: Apr 04 2011 06:18 AM
  • Joined: 03 May 2007
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.

xx3nvyxx
  • Members
  • 93 posts
  • Last active: Jan 21 2016 09:33 AM
  • Joined: 05 Sep 2005
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. :p

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.

xx3nvyxx
  • Members
  • 93 posts
  • Last active: Jan 21 2016 09:33 AM
  • Joined: 05 Sep 2005
#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.

xx3nvyxx
  • Members
  • 93 posts
  • Last active: Jan 21 2016 09:33 AM
  • Joined: 05 Sep 2005
;
; AutoHotkey Version: 1.0.47.5
; Language:       English
; Platform:       WinXP Pro
; Author:         William Washco <[email protected]>
;
; 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.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Why not simply...
Loop, %A_AppData%\.purple\logs\aim\*, 2
{
    Loop, %A_LoopFileLongPath%\randommsn\*.html
    {
        ;...
    }
}
:?:

xx3nvyxx
  • Members
  • 93 posts
  • Last active: Jan 21 2016 09:33 AM
  • Joined: 05 Sep 2005
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.

LBJ
  • Members
  • 24 posts
  • Last active: Apr 04 2011 06:18 AM
  • Joined: 03 May 2007

Why not simply...

Loop, %A_AppData%\.purple\logs\aim\*, 2
{
    Loop, %A_LoopFileLongPath%\randommsn\*.html
    {
        ;...
    }
}
:?:


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...

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
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:
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.

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.

LBJ
  • Members
  • 24 posts
  • Last active: Apr 04 2011 06:18 AM
  • Joined: 03 May 2007

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.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
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:
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? :lol:

xx3nvyxx
  • Members
  • 93 posts
  • Last active: Jan 21 2016 09:33 AM
  • Joined: 05 Sep 2005
...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.

LBJ
  • Members
  • 24 posts
  • Last active: Apr 04 2011 06:18 AM
  • Joined: 03 May 2007

It seems too simple... maybe I missed something? :lol:


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

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.

LBJ
  • Members
  • 24 posts
  • Last active: Apr 04 2011 06:18 AM
  • Joined: 03 May 2007

...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...

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
}



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.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

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...