Since at least OS3.1, AmigaOS has come with a couple of potentially useful scripts called SPat & DPat. Originally they were found in S:, but with OS4 they were moved to S:Shell.
SPat takes the following arguments: COM/A,PAT/A,OPT1,OPT2,OPT3,OPT4
This is quite useful, because it allows you to call any COMmand on all files that match the given PATtern (OPTionally supplying several parameters to the command), even though the command doesn't support pattern matching itself. For example, if you executed the following in the Shell:
S:Shell/SPat Type #?.txt NUMBER
Then it would execute the Type command on all .txt files in the current directory (giving the NUMBER parameter to Type, so that it shows line numbers).
UNFORTUNATELY it also has some rather major down sides: 1. It only works in the current directory. 2. The pattern matches both file AND directory names, which is not typically wanted. 3. It does not recurse into sub-folders.
I recently got around to properly fixing all these short-comings (I've had some half-baked solutions for years). This requires two scripts, which for the sake of simplicity I shall also put in S:Shell :
First new script I call SPatDF (where the D stands for Directory & F stands for File): Quote:
List >T:q{$$} "{DIR}" PAT "{PAT}" FILES SORT=N LFORMAT "{COM} *"%s%s*" {OPT1} {OPT2} {OPT3} {OPT4}"
IF NOT FAIL Execute T:q{$$} Else Echo "{DIR} not found" EndIF
Delete >NIL: T:q{$$} QUIET
FailAt 10
This matches the given PATtern on just *files* in the given DIRectory (use "" for the current directory), and then (like SPat) executes the given COMmand (with any OPTional parameters). As a bonus the files are handled in alphabetical order, thanks to the OS4-only SORT=N option, but otherwise the script should work on OS3 too.
The second new script is called SPatRDF (where the R stands for Recursive): Quote:
IF NOT FAIL IF "`FileSize T:qq{$$} FORMAT %s`" NOT EQ "0" Sort T:qq{$$} T:qq{$$} ENDIF Execute T:qq{$$} Else Echo "{DIR} not found" EndIF
Delete >NIL: T:qq{$$} QUIET
FailAt 10
This takes the same arguments as SPatDF, but this time it recursively scans all sub-folders, and then matches the given PATtern against the files in every folder. Note that directories are also scanned in alphabetical order. This script would probably work fine on OS3.
So for example, if I executed the following in the Shell:
S:Shell/SPatRDF Type "" #?.txt NUMBER
Then it would recusively match all .txt files in the current directory, and then execute Type on each of them. Or for example, I could execute the following in the Shell:
S:Shell/SPatRDF Type Work: #?.txt NUMBER
And then it would recursively match all .txt files in the Work: volume.
Other people may wish to improve these scripts further, but they work well enough for me already. One idea for a possible improvement: Make the DIR argument not required, in which case it would default to the current directory. This is fiddlier than it looks, due to how ReadArgs processes a mix of required & optional arguments, so I felt it better to just need "" for the current directory, rather than complicating the script's arguments.
@Severin I couldn't get the "SPatRDF Alternate:" script to recurse when I used a pattern like "#?.txt" so I don't think it would replace ChrisH's recursive script. Also, programs and scripts should return with the original current directory. In the case of a script that would mean using PushCD & PopCD instead of plain CD.
Amiga X1000 with 2GB memory & OS 4.1FE + Radeon HD 5450
Probably me using ForEach wrong. the docs are written by a dev for a dev. the script is pointless anyway. it's just as easy just to use the ForEach command directly eg.
ForEach i in #?.txt com "type *"${i.path}$i*" number" all files sort
(forgot to add the sort option in the original post.)
Amiga user since 1985 AOS4, A-EON, IBrowse & Alinea Betatester
@Severin I wasn't aware of the ForEach command, but something similar was sometimes useful in MS-DOS, so seems handy.
Quote:
the script is pointless anyway. it's just as easy just to use the ForEach command directly
I think you already proved that's not that case!: Quote:
Probably me using ForEach wrong. the docs are written by a dev for a dev.
I don't think my script could be any easier to use. ForEach does a lot more than my simple script, and thus is more complex to use.
@Marko Quote:
What does the names SPat and DPat stand for?
If you read the comments inside the original scripts, it's fairly clear that "S" stands for "Single argument command" & "D" stands for "Double argument command".
I've never used "Double", but it seems to allow you to supply both the original path of the matching file AND a revised path that is relative to the supplied DIR path. I guess you could use it for copying or comparing files in nested folders. In this case I think ForEach is likely a better & more flexible solution than DPat.
Probably me using ForEach wrong. the docs are written by a dev for a dev. the script is pointless anyway. it's just as easy just to use the ForEach command directly eg.
Yes, the docs aren't as clear and organized as they should be. There should be more examples with explanations for the examples.
Quote:
ForEach i in #?.txt com "type *"${i.path}$i*" number" all files sort
That line doesn't recurse either. I tested by replacing the "type" command with the "echo" command so I could easily see which files it's finding. It appears to be applying the pattern (#?.txt) to directory names and skipping the directories if they don't match the pattern. I tried naming a subdirectory "subdir.txt" and ForEach entered that directory.
I'd say ForEach recursion with a pattern is broken. I don't think it should be matching the pattern to directory names when the "FILES ALL" options are used. Thanks for bringing up the "ForEach" command because the recursion problem has gone unnoticed for a long time.
Amiga X1000 with 2GB memory & OS 4.1FE + Radeon HD 5450
I don't think my script could be any easier to use. ForEach does a lot more than my simple script, and thus is more complex to use.
As I pointed out to Severin, a recursive file pattern search using the "FILES ALL" options doesn't work with ForEach because it's matching the pattern with directory names too.
Amiga X1000 with 2GB memory & OS 4.1FE + Radeon HD 5450
I'd say ForEach recursion with a pattern is broken. I don't think it should be matching the pattern to directory names when the "FILES ALL" options are used.
Since the List command has exactly the same behaviour, I would say that this behaviour is "by design".
It's also why my SPatRDF script needs a second script to work: The outer script uses List to recusively find all the directories (without using a pattern!), while the inner script uses List to non-recursively find files matching the pattern inside a single directory.
Most likely you could replace List in both scripts with ForEach, but you will still need both scripts for it to work. (Or "inline" the inner script, but that makes the code less readable & less testable.)
Since the List command has exactly the same behaviour, I would say that this behaviour is "by design".
It's also why my SPatRDF script needs a second script to work: The outer script uses List to recusively find all the directories (without using a pattern!), while the inner script uses List to non-recursively find files matching the pattern inside a single directory.
Most likely you could replace List in both scripts with ForEach, but you will still need both scripts for it to work. (Or "inline" the inner script, but that makes the code less readable & less testable.)
Hmm. I don't think that's true. Enter this line in a shell:
list System:Documentation/#? PAT #?.txt FILES ALL LFORMAT "< file is > %F%N"
and it should print all 31 #?.txt files (with path) in the Documentation directory and all it's subdirectories.
Am I misinterpreting what you mean??
Amiga X1000 with 2GB memory & OS 4.1FE + Radeon HD 5450
Then I must have misunderstood the problem you were having with ForEach.
If you use a pattern with the FILES and ALL options, "list" will recurse and find all the the files that match the pattern in all subdirectories.
if you use a pattern with the FILES and ALL options "foreach", will NOT recurse and find all the the files that match the pattern in all subdirectories.
If there is a directory that matches the pattern, "foreach" will recurse into that directory when the FILES ALL options are used; which means that "foreach" is matching directory names to the pattern while the "list" command is not.
Amiga X1000 with 2GB memory & OS 4.1FE + Radeon HD 5450
I have reported the problem on the OS4 bugtracker.
Thanks. After ForEach was first included in SYS:C, I read the docs but really didn't 'get it' as far as it's potential usefulness; so I forgot about it.
If it were fixed and some more examples (with explanation) were added to the doc, it could be very useful for things like recursive file searches, file copying, file renaming, file numbering etc.
Amiga X1000 with 2GB memory & OS 4.1FE + Radeon HD 5450