glob [[switches]] pattern [[pattern ...]] Supported ''switches'' are: http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/glob.htm The [[glob]] command lists all the directory entries (files, directories, and links) whose names match a given pattern. If no files or directories were found, it throws an error, unless the '''-nocomplain''' switch is given. This models the behavior of [Unix]'s '''ls''': $ ls not_existing ls: not_existing: No such file or directory $ echo $? 1 ---- Note that one of glob's overlooked switches is the -type [http://www.tcl.tk/man/tcl8.5/TclCmd/glob.htm#M10] switch - with it, one can say, for instance, ** Documentation ** glob -type hidden * [http://www.tcl.tk/man/tcl/TclCmd/glob.htm%|%official reference]: and on unix, you will see all files beginning with a period (.), while on Windows and MacOS, you see truly ''hidden'' files. Conversely, it is worth knowing that under Windows [[glob *]] will return files named .*, as will [[glob *.*]]. There's no single option to force Windows to follow the Unix convention (of .* files being hidden) but you can use the form ?*.* to force the Unix behaviour. ---- It's usually best to build up the path with [[file join]] (see the [file] command for details), and then add a pattern after that. In Tcl 8.2 to list all the entries in a given directory, for instance, one would use ** Description ** [lsort [glob [file join $dir {{.*,*}}]]] '''`glob`''' lists all the directory entries whose names match a given (On Unix, Note that file names beginning with a period are hidden by default. The pattern above expands these hidden files as well as the ordinary ones). ======none ([NEM]: This pattern fails, at least in 8.4.7 and 8.5a3. It seems the {a,b} notation is for matching literal strings, not patterns. An alterative that appears to work is: [lsort [glob -dir $dir * .*]] ) [PYK] 2016-11-16: Except that it doesn't model the behaviour of ls in other However, this line actually has a bug in it: if $dir contains characters which are glob-sensitive (any of those square/curly brackets, stars, question marks etc), the above example will fail. Therefore in Tcl 8.3 or newer, you should ''always'' use: [lsort [glob -dir $dir {.*,*}]] which actually has the benefit of being clearer to read too! If you want to use this construction in older versions of Tcl, see [glob forwards compatibility] for a way to override the older [[glob]] command. ====== Indicate the end of the switch by "--" if your pattern contains "-". There are cross-OS issues with this (in particular the handling of hidden files), [LV] Actually, you should ALWAYS use the ''--'' ; there is no problem using it, and unless you are hardcoding literal arguments to glob you may accidentally get an argument beginning with a dash. To get a list of all contents of a directory that aren't themselves ---- The [[glob]] command can be used to simulate [ls -l in Tcl]. ---- In more recent versions of Tcl, glob even has a flag to specify whether one wants just directories or files returned. ---- [LV] does anyone have some tcl code for handling negated patterns? The [ksh] glob function allows me to do things like !(o*) or ab[[!b-z] and I would like to be able to do similar things. ---- '''Stale links:''' ''glob'' in older (pre Tcl 8.4a4) versions of Tcl has a funny behavior (seen on Solaris 5.5.1) with symbolic links that don't point to an existing file: % exec ln -s /does/not/exist stalelink % glob stalelink no files matched glob pattern "stalelink" % glob stalelink* stalelink If the pattern is not wildcarded, it tries to follow the link; otherwise it just returns the link's name itself. Hmm... ([RS]) ====== ---- [Tcl warts] One might consider at least one of glob's behaviors as a wart. glob uses the brace ({}) characters as special notation, even though the tcl parser ALSO uses these characters. The result is that one needs to learn, from the beginning, that one needs to quote these characters when using them in glob arguments. glob also uses the [[ and ]] characters and those need quoted as well. '''EE:''' Actually, I'm of the opinion that one should always "quote" or {brace} arbitrary strings that are being used as command arguments. [LV] I agree. However, not everyone does so. It would look weird to say things like string "length" $a or even somecmd "-length" "60" , wouldn't it? [PYK] 2014-08-13: edited the example above to use `[[[file tail] $item]]` '''EE:''' Yes it would, and I hadn't thought of that. But I draw a distinction between the parts of the command itself, and the rest, which are the arguments. For your examples, '''[string] length''' is really the command, and '''somecmd -length''' is really the command, and we generally don't quote the command. But I'll usually do '''string length "$a"''', or '''somecmd -length "60"'''. And I'll ''certainly'' do '''string compare "$stringone" "$string2"''', because it once took me two days to track down a bug caused by failing to put the arguments to a string compare into quotes. [RS] To the parser, the quotes just mean "group me" which is important if the quotes contain whitespace. If no substitutions are wanted, curly braces have the same effect, so "a rose" == {a rose} != a rose For strings without whitespace, quotes are redundant, but may still serve a documentation purpose for the reader (e.g. in [Salt and Sugar], sugar arguments are quoted to express that they are just sugar). (Note that these are mutually exclusive. Also note that `-directory` can be '''EE:''' Is that completely true? The bug I speak of above was in a case where I was doing string manipulation on a variable which contained 32 characters of purely digits, and it was erroring until I put the $variable into quotes. Let's say I want to find all files in a directory, then I use (ignoring hidden '''[LV]''' Likewise, the point I was trying to make above was that for glob (the subject of this page), one needs to do particularly special quoting to ensure that the parser doesn't touch the { and } characters, but lets them pass on to glob itself... '''Vince:''' fortunately with the use of '-dir' or '-path' flags to glob the need for quoting is pretty much gone in Tcl 8.3 or newer. ---- Anyone want to talk about the use of ''-directory'' vs ''-path'' ? Let's say I want to find all files in a directory, then I use (ignoring hidden files for the moment) ====== glob -dir $dir * Now, what I want all files in that directory whose names start with `$nametail` Now, what I want all files in that directory whose names start with $nametail (where I have no idea what characters are in $nametail). I could try: ====== glob -dir $dir ${nametail}* but that would fail (either with an error or not the expected result) if but that would fail (either with an error or not the expected result) if nametail contains characters like {}. Therefore I must use ''-path'', like this: ====== glob -path [file join $dir $nametail] * One thing to be aware of is that `-dir {}` is the same as `-dir .`. Hopefully Also, when to use ''-join''? If I want to find all *.tcl files in any subdirectory of a known directory, I could try this: ====== glob -dir $dir */*.tcl but that won't work on MacOS. So, use `-join`: but that won't work on MacOS. So, use -join: ====== glob -join -dir $dir * *.tcl Hope that helps! ** Trailing `/` ** ---- '''Recursive glob:''' This lightly tested procedure follows subdirectories (not sure what happens with links) and returns all non-directories in alphabetic order: proc glob-r {{dir .}} { foreach i [lsort [glob -nocomplain -dir $dir *]] { if {[file type $i] eq {directory}} { if {[file type $i]=="directory"} { eval lappend res [glob-r $i] lappend res $i } } return $res set res } ;# RS (Invocation without arguments verified works with 8.6.) [JH] May 16th 2006: With this variant that allows for optional patterns of interest, like '''glob-r C:/Tcl *.gif *.ps''': ====== proc glob-r {{dir .} args} { set res {} foreach i [lsort [glob -nocomplain -dir $dir *]] { if {[file isdirectory $i]} { eval [list lappend res] [eval [linsert $args 0 glob-r $i]] } else { if {[llength $args]} { foreach arg $args { if {[string match $arg $i]} { lappend res $i break } } } else { lappend res $i } } } return $res } ;# JH [MG] May 20th 2004 - I couldn't get the above code to work, so wrote my own version of it. The only oddity about it is that paths (sometimes) start '././', rather than just './' - I really don't know why. But using [[file exists]] on those files still works, so it doesn't really seem to matter. (It also uses [[info level 0]] to find out it's own name when it calls itself recursively; this is, simply, because I just found out how you do that :) Replacing ''[[lindex [[info level 0]] 0]]'' with ''globRec'' (or whatever you call the procedure) will speed it up a fair bit. ''Changed quickly to add -- into the [[glob]] calls, so $dir can't be mistaken as a switch, as suggested above''. Takes an optional second argument to specify which types of file to [[glob]] for; by default, all mentioned on the manpage apart from directories. proc globRec {{dir .} {types {b c f l p s}}} { set files [glob -nocomplain -types $types -dir $dir -- *] foreach x [glob -nocomplain -types {d} -dir $dir -- *] { set files [concat $files [[lindex [info level 0] 0] [file join $dir $x]]] } return [lsort $files]; };# globRec TC 23 Nov 2004 - I couldn't get either of the above functions to do what I wanted, which was to recursively list all directories and use a filespec. My TCL hack (lightly tested on 8.3 and 8.5) of [MG]'s code, which seems to do all his was intended to do also. Modified to also eliminate leading ./s. Modified to support filenames with spaces in them. Note: you may need to replace `[file join] $dir $x` with simply `$x`. I had to. Note: you may need to replace [file join $dir $x] with simply $x. I had to. ====== proc globRec {{dir .} {filespec "*"} {types {b c f l p s}}} { set files [glob -nocomplain -types $types -dir $dir -- $filespec] foreach x [glob -nocomplain -types {d} -dir $dir -- *] { set files [concat $files [globRec [file join $dir $x] $filespec $types]] } set filelist {} foreach x $files { while {[string range $x 0 1]=="./"} { regsub ./ $x "" x } lappend filelist $x } return $filelist; };# globRec [jys] – I wanted a recursive glob that would take the same options as glob, so I wrote this wrapper. I don't know if it works 100% with all options though, and it currently gets tripped up by glob spitting the dummy when it can't look inside a directory (generally 'cause of permissions). proc rglob {args} { # This code requires -join and -nocomplain. We also remove the -types option, and then manually apply a filter at the end. set args [linsert $args 0 -nocomplain -join] if {[set types_index [lsearch -exact $args -types]] != -1} { set types [lindex $args [expr {$types_index+1}]] set args [lreplace $args $types_index [expr {$types_index+1}]] } # Get inital matches. set matches [glob {*}$args] # Keep adding * wildcards to search through subdirectories until there are no more levels of directories to search. while {[llength [glob -types d {*}[lreplace $args end end *]]] > 0} { set args [linsert $args end-1 *] lappend matches {*}[glob {*}$args] } # Filter matches with -types option. if {[info exists types]} { set matches [glob -nocomplain -types $types {*}$matches]} return $matches } ---- [ES]: '''findfile:''' This procedure was inspired by glob-r above and is customized to quickly find the first instance of a file in a given path. proc findfile { dir fname } { [llength [set x [glob -nocomplain -dir $dir $fname]]] } { return [lindex $x 0] } else { foreach i [glob -nocomplain -type d -dir $dir *] { if { $i != $dir && [llength [set x [findfile $i $fname]]] } { return $x } } } } } (Verified works with 8.6.) ---- [MSW]: [pathentry]: Entry widget/binding set using glob to offer quick auto-completion of paths ---- [GPS]: In the Tcl'ers chat this elegant solution was created by [DKF]: proc * args {glob -nocomplain *[join $args {}]} ([CMCc]: Now try the same kind of composition and extension under /bin/sh :) [MJ] - Shouldn't this be: proc * args {glob -nocomplain *\{[join $args ,]\}} Because [DKF]'s version doesn't do what I expect it to do with multiple args (maybe my expectation is wrong) ---- [EB]: The [changes in Tcl/Tk 8.4.9] shows that ''tilde paths are not returned specially by [glob]'', but shouldn't that have been done to avoid a security problem with ~ expansion ? e.g: foreach file [glob "*"] { file delete -force $file } where [glob] returns a file beginning with a ~ ? [Vince] writes: for historical reasons the code above is considered wrong/buggy. You should write ''file delete -force ./$file''. This is perhaps something that should be changed in the future, but it might well break some old code. There is even a bug report or patch somewhere on sf about this, I think (probably closed now). ---- See also here: [Matthias Hoffmann - Tcl-Code-Snippets]! ---- Here is a proc that will list the directory tree under any given root, giving the full path name of all descendants. It embeds glob in a recursive call; a directory with no descendants is the base case. ====== proc listTree {rootdir_} { # Precondition: rootdir_ is valid path set currentnodes [glob -nocomplain -directory $rootdir_ -types d *] if {[llength $currentnodes] <= 0} { # Base case: the current dir is a leaf, write out the leaf puts $rootdir_ return } else { # write out the current node return # Recurse over all dirs at this level foreach node $currentnodes { listTree $node } } } listTree [pwd] listTree [pwd] (Verified works with 8.6 (and is surprisingly quick).) That this isn't a Tcl built-in (or if it is, why is it is such a well-kept secret) strikes me as odd. I remember reading something about a pre-defined limit on depth of recursion in I remember reading something about a pre-defined limit on depth of recursion in Tcl, so this code is not guaranteed for arbitrarily deep file structures. Bob [kostix] (04-Sep-07) notes that there's one trick with '''glob''' on Windows: glob -dir c: * will list contents of ''the current working directory on drive C:,'' not the contents of the root folder on that drive. ====== glob -dir c:/ * ====== The described behaviour is consistent with what is seen in '''cmd.exe''' command shell. The described behaviour is consistent with what is seen in '''cmd.exe''' [Zipguy] 2013-02-07 - Thanks [kostix] for pointing this out. I do use windows ---- [Sarnold]: Sometimes [glob]-like patterns are expected to be passed to some commands. This may lead to bugs, for example, in [http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/a2021acab428e198/b79eadada5bc5f3b#b79eadada5bc5f3b]: ** Stale links ** set bar {x[y]} set foo($bar) yes array unset foo $bar parray foo => foo(x[y]) still exists If the pattern is not wildcarded, it tries to follow the link; otherwise it What's the cure, Doctor? '''Unglob it!''' proc unglob {x} { string map {* \\* ? \\? [ \\[ ] \\]} $x } ---- In a recent email thread on the [Modules] mailing list, a developer reported a change he found when beginning to use Tcl 8.5: [Vince] writes: for historical reasons the code above is considered === $ tclsh8.4 % glob /home/ /home % glob /home /home ^D $ tclsh8.5 % glob /home/ /home/ % glob /home /home ^D === Turns out that the modules package was dependant on having the path returned without the trailing '''/''', and when 8.5 started returning some paths with the slash, certain behaviors changed. From a purely technical point of view, the two forms of the name are considered equivalent by the operating systems with which I am familar (Ultrix, Solaris, etc.). I don't know if there are differences for variants or not. ---- !!!!!! [Tcl syntax help] | [Arts and crafts of Tcl-Tk programming] %| [Category Command] | [Category File] |% !!!!!!