. . . .

Windows Scripting Examples

 

updated:2018.05.13

Prev   Next   Site Map   Home  
Text Size
Please reload page for style change

IMPROVED CD


rem File: d.bat
if _%1 == _. goto cdDone
if _%1 == _? goto help
if /i _%1 NEQ _-H goto doCd
:help
echo d.bat and dSup.py 2016.04.23. Extended chdir.
echo -C = clear traversal list.
echo -B or nothing = backward (to previous dir).
echo -F or - = forward (to next dir).
echo -R = remove current from list and return to previous.
echo -R dir = remove dir from list.
echo -S = select from list.
echo -H = help.
echo . = make window title current directory.
echo Anything else = target directory.
goto done

:doCd
%~dp0dSup.py %*
for /F "delims=" %%d in ( %HOME%\dSupList ) do (
    pushd %%d 
rem pushd is used instead of cd because it changes drive. cd changes current 
rem directory of another drive but doesn't change drive.
    if errorlevel 1 ( %~dp0dSup.py -R "%%d" )
    goto cdDone
)
:cdDone
title %CD%
:done

DESIGN

See companion Python script dSup.py and equivalent Bash script cdx

The improved chdir for Windows is a relatively complicated script well beyond the capabilities of BAT. d.bat is very simple because it serves only as a shell for the main script dSup.py, written in Python. The Python interpreter executes in a child environment and cannot change the current directory of the parent. d.bat can invoke dSup.py and can change the directory but the only return value it can accept is a single integer ERRORLEVEL. Python can’t change anything in the parent environment so that avenue is also blocked. Thus, the only mechanism available for conveying the target directory to the BAT script is a real file.

The Python script could write just the target directory to a file, which the BAT script could subsequently read, but it also needs a means of storing the directory traversal list. Here too the environment offers no help because any changes to it persist only for the duration of the current invocation. Again, the only viable mechanism is a real file.

A single file can serve both requirements. Each directory path in the traversal list is written as one line in the file with the target always the first. for /F "delims=" %%d in ( %HOME%\dSupList ) do ( reads this into a local variable, which can be dereferenced to change the directory.

USE

d [directory | -C | -B | -F | -R | -S | -H]. If directory is . then this just assigns current directory to the command window title, which is also done in all other command cases as well.

dSup.py and d.bat are colocated, normally in C:\CmdTools. The directory traversal history file is %HOME%/dSupList. This doesn’t have to already exist but we have to be able to R/W it, i.e. %HOME% must exist. d.bat fails if invoked in an archive/backup directory for BAT files because dSup.py won’t be there. If in a command window in such a directory, either close the window or cd out of it.

d without arguments changes to the most recent directory. Doing this whenever a command window is opened provides workplace persistence while reducing disk wear, because dSupList is not rewritten when it doesn’t change, as in this case. The cmd link target is %SystemRoot%\system32\cmd.exe /K d.bat. The shortcut’s StartIn directory should be one that is rarely used in a cmd window, e.g. C:\Windows, because it cannot persist, as d means to go to previous.

DIRECTORIES

The list of recently visited directories is stored in dSupList file, one per line. dSup.py always puts the target on the first line, enabling this file to serve as a pipe from dSup.py to d.bat. d.bat always assumes this and tries to cd to that first line. In some cases, this is just the current directory, essentially doing nothing, which is simpler than devising some magic handshake syntax.

dSup.py doesn’t try to determine whether a requested target directory exists or can be moved into but simply puts it into the list. If the resulting cd fails d.bat recalls dSup.py with the -R argument, asking the head of the list (the failed directory) to be removed. This is not the most efficient mechanism but it does afford the simplest code.

The first line of the directory list file is extracted with a for /F statement, which jumps out after the first iteration, parsing out the first line. BAT doesn’t have another means of doing this. The option delims= says that there are no delimiters, telling for to not parse the line into separate arguments. Without this, the line would be parsed on space, truncating directories containing space.



IMPROVED WILD CARD FILE RENAME


' renx.vbs
' Updated: 2013.08.17
' Author: David McCracken
' Purpose: Windows vbscript extended file rename. This renames files in the 
' current directory with more complete wildcarding than the standard ren 
' command. For each file name that passes the filter pattern, the name 
' fragments corresponding to *s in the filter replace corresponding *s in the 
' replacement pattern. All fragments matching the filter pattern are replaced 
' by corresponding fragments from the replacement pattern.
'
' .................... WSH .....................................
' Both wscript and cscript work with this but wscript's presentation of 
' optional show information is clumsy. The only way to implicitly force 
' cscript to execute is to make cscript the default: 
' wscript //H:cscript
' Only the script's UI is affected. GUI programs execute the same way 
' whether invoked by wscript or cscript.
' To suppress cscripts annoying logo:
' cscript //S //Nologo
'
' ................... Use ......................................
' renx filter replacement [show] Rename, using replacement, all files in 
' current directory that match filter. If optional show is any digit, the 
' name of each file and its replacment, if it passes the filter, are echoed. 
' If show is 0 then the files are not actually renamed. This preview can help 
' avoid accidentally losing useful name fragments. For files that pass the 
' filter, the name fragments corresponding to the filter's * characters 
' replace *s in the replacement pattern in the same order. If the original 
' name has fewer of these keepers than the replacement has *s then the 
' unmated *s are replaced by empty. If the original name has extra keepers, 
' these are concatenated as the last replacement. This rule is arbitrary but 
' often useful.
'
' ................. Design ......................................
' The two basic approaches are to parse the three strings, filter, replacment,
' and the file under test individually and then build the replacement or to 
' scan all three simultaneously while building the replacement. The latter 
' could be more efficient due to less superfluous data movement but is a more
' complex program. Some intermediate approach might be a good compromise but 
' I have taken the easy way, which is to parse the three strings as 
' independently as feasible before merging.
' .................... filter and replacment fragment arrays ..............
' Split is used to make the filter fragment array aFilter and the replacment 
' array aRep. The filter is not case-sensitive so the fragments are all 
' upper-case and will be compared to upper-cased file names. The actual file 
' name is used for keepers. To make the distinction clear, file.name is used 
' for replacment while filterName, an upper-cased copy is used for filter 
' testing. aFilter and aRep are created once and then used for all files 
' under test.
' ........................ new name merge .........................
' An array of keeper fragments is built while testing a file name against the 
' filter fragments. The new name is subsequently constructed by interleaving 
' replacement and keeper fragments. Which merge source goes first could depend 
' on whether the first character of the replacement pattern is *. However, this 
' question is side-stepped by the fact that if the first replacement character 
' is *,  Split produces an empty first element, which appends to the new name 
' as nothing. Thus, the replacement can go first in all cases.
' ....................................................................

const ShowNone = 0, ShowAll = -1, ShowFile = 1, ShowFilter = 2, ShowFail = 4
const ShowKeep = 8
Show = ShowNone ' This can be overridden by optional show argument.
noRename = false ' May be overridden by show argument.
set shell = CreateObject("WScript.Shell")
set fso = CreateObject("Scripting.FileSystemObject")
srcdir = shell.CurrentDirectory

helpLines = Array ( _
"renx.vbs renames files in the current directory.", _
"renx filter replacement [show]", _ 
"File names that pass filter are renamed according to replacement.", _
"Optional argument show:", _
"  0 = show old/new names while renaming.", _
"  1 = show names but don't rename files.", _
"  2 = show analysis but don't rename files.", _
"Fragments of the old name corresponding to filter *s replace replacement *s", _
"in the same order. Extras are concatenated for the last replacement.", _
"Examples:", _
"renx 08493357-0*.PNG hap*.png renames 08493357-001.PNG hap01.png", _
"  and skips 08493357-001.PNGX", _
"renx 0*93*7-0*.P* hap*ong renames 08493357-001.PNG hap843501NGong", _
"  and 08493357-001.PNGX hap843501NGXong")

if WScript.Arguments.Count < 2 then
    for each line in helpLines
        WSH.Echo line
    next
    WScript.quit
end if
if WScript.Arguments.Count > 2 then
    Show = ShowFile
    if WScript.Arguments(2) <> 0 then
        noRename = true
        if WScript.Arguments(2) > 1 then
            Show = ShowAll
        end if
    end if
end if

aFilter = Split( ucase( WScript.Arguments(0)), "*" )
aRep = Split( WScript.Arguments(1), "*" )
nRep = UBound( aRep )
if nRep < 1 and UBound(aFilter) > 0 then
    WSH.Echo "Replacement must have at least one * if filter has any."
    WScript.quit
end if 
dim aKeep(20)
dim nKeep

for each file in fso.GetFolder( srcdir ).Files
    filterName = ucase( file.Name )
    if Show and ShowFile then WSH.Echo "Checking " & file.name end if
    pass = true
    pos = 1
    nKeep = 0
    for idx = 0 to UBound( aFilter )
        frag = aFilter( idx )
        if frag <> "" then 
            match = InStr( pos, filterName, frag )
            if match < 1 then
                if Show and ShowFail then WSH.Echo "Fails on " & frag end if
                pass = false
                exit for
            end if
            if Show and ShowFilter then WSH.Echo "Passes " & frag end if
            if idx > 0 then
                keep Mid( file.name, pos, match - pos )
            end if
            pos = match + Len( frag )
        end if ' frag <> ""
    next ' for idx = 0 to UBound( aFilter )

    if pass and pos <= Len( filterName ) then
        if aFilter( UBound( aFilter )) <> "" then
            if Show and ShowFail then 
               WSH.Echo "Fails on trailing " & Mid( file.name, pos ) 
            end if
            pass = false
        else
            keep Mid( file.name, pos )
        end if ' aFilter( UBound( aFilter )) <> "" 
    end if ' pass and pos <= Len( name )

    if pass then 
        idx = 0
        newName = ""
        for each frag in aRep
            newName = newName & frag
            if idx < nKeep then
                newName = newName & aKeep( idx )
                idx = idx + 1
            end if
        next
        if Show and ShowFile then WSH.Echo "New name is " & newName end if
        if noRename = false then
            file.name = newName ' Rename the actual file object
        end if
    end if
next ' file in fso.GetFolder( srcdir ).Files

' ------------------ keep -------------------------------
sub keep( frag ) 
dim idx
' If there are more keepers than slots for them in the replacement pattern
' then concatenate all extras into the last keeper, which will subsequently
' fill the last replacement slot.
if nKeep >= nRep then
    idx = nKeep - 1
    aKeep( idx ) = aKeep( idx ) & frag
else
    idx = nKeep
    aKeep( nKeep ) = frag
    nKeep = nKeep + 1
end if
if Show and ShowKeep then WSH.Echo "keep " & aKeep( idx ) end if
end sub ' keep

MOTIVATION AND GOAL

Windows’ command line wild card capability affords only rudimentary file group renaming. For example, patent office image files have long numeric names like 08493357-001.tif, which I would like to replace with shorter and more intuitive names. Windows documentation implies that the command ren 08493357-0*.tif hap*.tif would rename the series hap01.tif, hap02.tif, etc. Instead, the new names are hap93357-001.tif, etc. Similarly, ren 08493357-0*.tif *.tif reproduces exactly the same name as the original. There is no way to remove pieces of a name but only to replace them with other characters. A better rename facility would replace each * in the new name pattern with the * phrase in the original, retaining the literals in the new pattern while discarding them in the original. For example:
ren 08493357-0*.tif hap*.tif produces hap01.tif, etc.
ren 08493357-0* hap* produces hap01.tif, etc.
ren 08493357-0* * produces 01.tif, etc.
ren *93357-0* * produces 08401.tif, etc.

INLINE COMMAND

Windows dir command interprets wild cards much better than ren. With the patent image files, for example, dir *93357-0* shows all of the expected tif files as well as png files that also match this pattern. dir *93357-0*.tif shows only the matching tif files. Using dir to filter the names and piping into a more capable string processor like AWK can come close to the desired outcome. For example:
dir /b 08493357*.tif | awk "{name=$1; sub(/08493357-0/, \"hap\", name); system(\"ren \" $1 \" \" name) }"
This achieves the desired goal but is too complicated to serve as a routine command.

RENAME SCRIPT

An AWK script could be written to simplify usage but a bat file would still be needed to invoke the awk script unless Windows is configured to run awk scripts automatically. With the bat/awk pair we would have a choice between filtering and piping in the bat or using awk to filter. In either case, both the filter and replacement pattern have to be passed to the AWK script to tell how to replace fragments. Using WSH (Windows Scripting Host) functions, the same result can be achieved by a single vbscript or jscript. I elected to use vbscript for this because of its less complicated WSH interface.

DESIGN

Whether the rename script processes one file passed to it by an external filter, i.e. dir, or filters the directory itself, the core is the same. It has three input components, filter pattern (which is needed for replacement even if the script doesn’t do the filtering), replacement pattern, and one old file. From these, it produces a new name. The script really only has to do this and could pass the name on to some other process to do the actual renaming but it is convenient for the script to do this final step.

Conceptually, the new name is created by scanning the old name for the literal fragments of the filter pattern, replacing each * in the replacement pattern in order with the original intervening fragments. The filter pattern and old name are scanned without backtracking. To match the filter, the old name must have the same fragments in the same order. The old name’s intervening strings, which replace * in the filter pattern may be empty. This is the standard regular expression interpretation of *, “zero or more instances”. We could say that in this application there must be at least one character in the old name to replace each * but that probably would not be better and might be worse. Usage may clarify this. Changing the behavior would be trivial.

The fragments of the old name that match the filter are called match fragments and the intervening ones the keep fragments or keepers. Generating the new name by interleaving replacement and keeper fragments is conceptually trivial if the old name contains the same number of keepers as there are *s in the replacement. How best to handle mismatches requires some experience but it seems that the replacement must always be fully processed. If the keeper list is exhausted but the replacement contains more *s then there are empties, concatenating the surrounding replacement fragments. If an old name (which passes the filter) contains more keepers than there are *s in the replacement, an arbitrary but useful rule is that all of the extras are concatenated to fill the last replacement slot. This does not affect the filter. For example, the original name 08493357-001.PNGX does not pass the filter in the command renx 0*93*7-0*.PNG hap*.png but it does pass the filter in the command renx 0*93*7-0*.P* hap*.png and is renamed hap843501NGXYZ.png.

At some point the three strings are parsed. In each case parsing can occur separately or as part of a broader process. For example, we could parse the filter while parsing the old name. We could parse the replacement pattern while composing the new name. We might parse all three while generating the new name. The only part of this process where the three parses would not synchronize easily is in the keeper fragment determination because we don’t know the length of a keeper until we find the next filter fragment match. This requires delaying appending the keeper to the new name until the next match cycle. But one of the easiest ways to handle who appends first, replacement or keeper, when the replacement has a leading * is to split the replacement so that this case produces a blank, which appends to the new name as nothing, enabling the replacement to always go first. But if the keeper is delayed until the next match, the match must go first. This could be resolved by, at each iteration of the loop, first analyzing the next fragment of the filter and old name and then generating the new name fragment from the keeper start determined in the previous iteration and the keeper end determined in the current iteration.

The program would be less efficient (more unnecessary data movement) but simpler if the old name were parsed (using only the filter pattern) and the fragments saved (presumably in an array) and then the new name generated from the replacement pattern while stepping through the keeper array to replace each *. This also enables using an existing split function to parse both the filter and replacement patterns so that only the old name needs a unique parse process. A more efficient unified parse-generate process would not be especially difficult but simplicity is usually more important than performance for scripts in general and certainly in this case.

PROCEDURE

TESTING

Verify that with a directory containing 08493357-001.PNG, etc. and 08493357-001.PNGX:



AUTOMATING PAINT


' File: tifToPng.vbs
' Updated: 2013.08.18
' Purpose: Windows Script Host vbscript to make a png file copy of every tif 
' file in the current directory, optionally stretching the image.
'
' ............................ Use ..........................................
' tifToPng [stretch]
' stretch is a percentage of original. 100 is the original size. The minimum
' is 1, which is 1%, i.e. a 99% reduction.
' Invoke this in the directory containing the files to be converted. It can 
' be invoked directly or as an argument to WSH. Most conveniently, if located
' in the exe path, it can be invoked by root name tifToPng.
' ........................... Design .....................................
' Mspaint is controlled by open-loop key stuffing to the shell, which can make 
' a mess if an unexpected app activates at some point. It is best to close all 
' apps before running this. A confirmation dialog pops up before overwriting a 
' (PNG) file. The user must respond to this within one second to maintain sync. 
' It is best to delete any such files before running the script.
' .......................... notes .................................
'                      Source/Destination Directory
' This uses shell.CurrentDirectory directly. To take an optional path command 
' line argument with default to CD use:
' if WScript.Arguments.Unnamed.Length < 1 then
' srcdir = shell.CurrentDirectory
' else
' srcdir = WScript.Arguments(0)
' end if 
'                        Blocking/Unblocking SendKeys
' By default SendKeys blocks. For unblocking the optional Wait argument is 
' false. In most instances we want unblocking so that the script can send 
' keystrokes to the app. Telling mspaint to save the file is the one 
' exception. We would like to interact with the overwrite confirmation 
' dialog. Unfortunately, in this instance control returns immediately to the 
' script regardless of the specified or default behavior. The script's one-
' second delay at this point is longer than needed for the write to complete 
' in order to give the user an opportunity to respond to the query. The delay 
' could be longer but that would delay every write unnecessarily. This is the
' dumbest solution but easy to implement. 
' ............................................................................
helpLines = Array ( _
"tifToPng.vbs invokes mspaint to convert all tiff files in the current", _
"directory to png. The one optional argument causes the image to be stretched", _
"e.g. 50 is 50%, 100 is the original size, 150 is 150%.")
stretch = 100
set fso = CreateObject("Scripting.FileSystemObject")
set shell = CreateObject("WScript.Shell")

if WScript.Arguments.Count > 0 then
    if IsNumeric( WScript.Arguments(0)) then
        if WScript.Arguments(0) > 0 then
           stretch = WScript.Arguments(0)
        else
            WSH.Echo "Stretch must be > 0."
            WScript.quit
        end if
    else
        for each line in helpLines
            WSH.Echo line
        next
        WScript.quit
    end if
end if

shell.Run "mspaint ", 1
WSH.Sleep 1000

for each file in fso.GetFolder(shell.CurrentDirectory).Files
    if ucase(fso.GetExtensionName(file.Name)) = "TIF" then
' Type Alt-F O for File > Open
        shell.SendKeys "%fo", false 
        WSH.Sleep 500
' Type source file name into name field. We don't need to move to the field
' because this is automatic when the dialog opens.
        shell.SendKeys file.Path, false 
' Type Alt-O for Open button
        shell.SendKeys "%o", false 
        WSH.Sleep 500
' Now we are back in Paint's main window

        if stretch <> 100 then
' Type Alt-I, S for Image > Stretch. Hot key Ctrl-W doesn't work.
            shell.SendKeys "%IS", false
            WSH.Sleep 500
' Automatically in Horizontal stretch edit. 
            shell.SendKeys stretch
            shell.SendKeys "{TAB}" ' TAB to Vertical stretch edit
            WSH.Sleep 100
            shell.SendKeys stretch
            shell.SendKeys "{ENTER}"
            WSH.Sleep 500
        end if ' stretch

' Type Alt-F A for File > Save As
        shell.SendKeys "%fa", false 
        WSH.Sleep 500
' Type P(ng) Save. No Wait arg defaults to true.
        shell.SendKeys "%tp%s" 
        WSH.Sleep 1000
    end if ' TIF
next ' file
WSH.Sleep 500
shell.SendKeys "%fx", false ' Exit Paint

PURPOSE

Windows Paint program does a good job of translating a wide variety of graphic file formats with its Save As command. It also affords simple but effective editing, particularly cropping and resizing. However, it affords only direct interactive control, limiting its usefulness. It is simply too tedious to use for repetitive manipulation of more than one or two files. It is not intended for serious production, which can justify a professional program. But many users have very light production needs for which such a program is overkill but which are tedious to do interactively.

In this example, to include my own patent images, which are only available as TIFF, in a web page, I wanted to convert the files to png. I also wanted to shrink the images, which are twice as large as appropriate for web viewing. For one file, Paint would be ideal. I had more than a few files to process but I did not intend to go into production.

DESIGN

WSH (Windows Scripting Host) provides the means to invoke and control programs that, like Paint, afford no automation facilities, but they have to be used carefully. Using the WSH Exec function, a script can invoke and get status information about an external program, allowing some closed loop coordination. GUI programs, like Paint, cannot be launched by Exec but only by Run, which affords no such coordination. Any script launching Paint must run open-loop, with delays at least long enough to guarantee that Paint has finished the previous command before sending another. The commands themselves are sent through the WSH function SendKeys. It is easy to send key events to whatever program is listening but very difficult to force or even know for sure what program will actually receive these keystrokes. Further, SendKeys has an optional Wait argument, which is true by default, making the call blocking, which is unacceptable if the script must send additional events. However, whether or not the call actually blocks depends on application context. The effect is not the same for the main target program, its own dialogs, and any common shell dialogs (such as file open, in this example) the application invokes.

To keep this application simple, certain limitations are imposed. All tif files in the current directory are converted. No other directory can be specified and files are filtered strictly on the extension tif (case-insensitive).

The script could operate on one given file, which could be provided by another program or command, such as dir. However, this might create additional uncertainty in the key stuffing interface between the script and Paint, making it especially vulnerable on large file sets. I have chosen instead to have the script get the current directory from the shell and use the file system object to iterate over this.

set fso = CreateObject("Scripting.FileSystemObject")
set shell = CreateObject("WScript.Shell")
for each file in fso.GetFolder(shell.CurrentDirectory).Files

OPEN-LOOP KEY STUFFING

This script has performed reliably even given the problematic nature of open-loop operation and key stuffing uncertainty. It is a reasonable model to guide the creation of similar scripts for other uses of Paint and other similarly automation-challenged programs. Some general design suggestions derived from this are:



BUILD SUBVERSION REPOSITORY


rem makeSvnRepo.bat

if exist log del log

echo Create Subversion repository Svn7kFw >> log
md Svn7kFw >> log
svnadmin create Svn7kFw >> log

echo Create Svn7kFw/Src from the Unified code set >> log
svn import Unified %1/Svn7kFw/Src -m "Unified coding venue" >> log

echo Create Ver and Alt folders >> log
svn mkdir %1/Svn7kFw/Ver -m "Unified versions" >> log
svn mkdir %1/Svn7kFw/Alt -m "Unified alternatives" >> log

echo Copy the Unified code from Src to Ver/2.0 to capture version U2.0 >> log
svn copy  %1/Svn7kFw/Src  %1/Svn7kFw/Ver/2.0 -m "Unified code 2.0" >> log

echo Create Polycom alternative >> log
svn mkdir %1/Svn7kFw/Alt/Polycom -m "Polycom alternate program design" >> log

echo Copy unified code u0 as the first Polycom version >> log
svn copy %1/Svn7kFw/Src  %1/Svn7kFw/Alt/Polycom/Src -m \
"Unified 2.0 baseline for Polycom" >> log

echo Create Polycom Ver and Alt folders >> log
svn mkdir %1/Svn7kFw/Alt/Polycom/Ver -m "Polycom versions" >> log
svn mkdir %1/Svn7kFw/Alt/Polycom/Alt -m "Polycom alternatives"  >> log

echo Capture Polycom version 2.0 = u0 >> log
svn copy %1/Svn7kFw/Alt/Polycom/Src %1/Svn7kFw/Alt/Polycom/Ver/2.0 -m \
 "Polycom 2.0 is Unified 2.0" >> log

@ rem Prepare Polycom version P2.1. The current version, which is really the u0 
@ rem (version U2.0) code set, is first checked out just to prepare the 
@ rem Subversion client venue. Then all of the checked out code is discarded and 
@ rem replaced by the Polycom-Unified code set. This is then checked in 
@ rem (committed). Then it is captured into version 2.1

@ if exist src/nul RD /S /Q src
@ rem md src -- not needed. svn checkout creates Src (like it or not)
svn checkout %1/Svn7kFw/Alt/Polycom/Src >> log
del /Q Src
copy PolyUni Src >> log
svn commit Src -m "Touch Apps Internal\exchange\firmware\DJ\\
Gestures\preliminary\48pin_Polycom_Unified with merged includes" >> log
svn copy %1/Svn7kFw/Alt/Polycom/Src %1/Svn7kFw/Alt/Polycom/Ver/2.1 -m \
"Polycom version 2.1 is Touch Apps Internal\exchange\firmware\DJ\Gestures\
\preliminary\48pin_Polycom_Unified with merged includes" >> log

@ rem Prepare, commit and capture Polycom 2.2. This procedure is the same as use 
@ rem for version 2.1 except that the initial checkout to the temporary Src is 
@ rem not needed, as the directory is already in the required state.
del /Q Src
copy Polycom Src >> log
svn commit Src -m "Touch Apps Internal\Firmware Released to field\Polycom\\
2011-0701 Polycom v15 Faster FrontEndFilter\2011-0701 Polycom v15" >> log
svn copy %1/Svn7kFw/Alt/Polycom/Src %1/Svn7kFw/Alt/Polycom/Ver/2.2 -m 
"Polycom version 2.2 Touch Apps Internal\Firmware Released to field\Polycom\\
2011-0701 Polycom v15 Faster FrontEndFilter\2011-0701 Polycom v15 with merged \
includes" >> log

See process diagram

This builds a Subversion repository for an existing project with numerous branches and no existing version control. Knitting these into a unified project repository requires a long sequence simulating what should have been done in the first place, that is choosing a trunk, creating branches, and merging the branches back into the trunk.

Normally, there would be no reason to script this process because it would have evolved over time. However, in this situation, the process is very complex and likely to have errors until completed and fully tested. It was much faster for me to be able to automate the entire process as I developed and tested it so that I could easily scrap the test repository and start over.

The fact that the programmers had ongoing developments while I designed and tested this process meant that I was working with constantly changing sources so any repository that I did build was almost immediately invalid. Further, to start using the new version control I would have to tell all of the programmers to stop working while I built it and switched them over to it. By scripting everything and fully testing the results in a sandbox, I was able to suspend their work for less than 10 minutes, which the new repository was built from current sources and the programmers were all transparently switched over to it.

This is a batch file that creates the initial repository as a local file from a set of three original source folders. The three source folders, Unified, PolyUni, and Polycom contain fully prepared sources, including endstep.bat, .cproject, and .project Eclipse files. The include files have been moved into the source folder.

The repository is created at the same level as the three source files. The batch file is invoked with the URL of this working directory as a parameter to avoid the complexity of DOS path conversion to URL.

makeSvnRepo.bat: Make Svn7kFw Subversion repository from Unified, Polycom-Unified, and Polycom sources. This requires a command argument, which is the URL version of the CWD (%CD% in XP batch). i.e. file:///C:/path... The Svn7kFw Subversion repository will be created under CWD. This requires the source folders, Unified, PolyUni, and Polycom, to exist in CWD and to contain only the files that will be under version control, *.c, *.h, 7000scrpt.ld, ".project", ".cproject", and endstep.bat.



VERSION RELEASE PACKAGING


rem getCe60.bat
for /F "tokens=1,2" %%p in (..\Specific\ProjList) do ( 
    echo ......... Project %%p ...........
    md %%p > nul 2> nul
    cd %%p
    !vc! WorkFold "$/WinCE/CeProgs/%%p" !CD!
    echo. 
    
    rem Get the development version of project control files. Most or all
    rem of these will be overwritten by the User versions but getting these
    rem relieves the User domain of having to create duplicates where there
    rem is no difference (makefile is the most likely one of these).
    
    echo Getting development configuration files for %%p version %%q from CeProgs
    for %%f in (bib,pbpxml,reg,pbcXml) do (
        !vc! get -Vl%%q -W "$/WinCE/CeProgs/%%p/%%p.%%f"  
    )
    !vc! get -Vl%%q -W "$/WinCE/CeProgs/%%p/sources"   
    !vc! get -Vl%%q -W "$/WinCE/CeProgs/%%p/makefile"  
    
    rem Get User versions of these files over the development versions. These are 
    rem release-specific rather than project version-specific and may change very 
    rem little even between releases.
    
    !vc! WorkFold "$/WinCE/User/CE60/%%p" !CD! 
    echo.
    echo Getting User configuration files for %%p release version !ReleaseVer! 
    !vc! get -Vl!ReleaseVer! -W "$/WinCE/User/CE60/%%p"   

    rem Get dll, exe, pdb, and rel files for every supported CPU
    for /F %%c in (..\..\Specific\CpuList) do (
        echo Getting files for %%c
        md %%c > nul 2> nul
        cd %%c
        !vc! WorkFold "$/WinCE/CeProgs/%%p/CE60/%%c/retail" !CD! 
        echo.
        !vc! get -Vl%%q -W "$/WinCE/CeProgs/%%p/CE60/%%c/retail"   
        if errorlevel 1 (
            echo Unable to get version %%q of %%p %%c retail components
            exit /B 1
        )
        rem Return to project directory to process the next cpu
        cd ..
    )
    rem Return to BuildDir directory to process the next project
    cd ..
)
rem We are now at BuildDir directory. 

echo Getting User general installation programs for release version !ReleaseVer!
!vc! WorkFold "$/WinCE/User/CE60" !CD! 
echo.
!vc! get -Vl"!ReleaseVer!" -W "$/WinCE/User/CE60"  

echo End GetCE60.bat
rem Return to CePack

getCe60.bat is one component in a hierarchical system of scripts that automates building a Windows CE release package. The entire process fully automates clearing all residuals from the build computer, checking out of version control all sources, including the build files themselves, and building a suite of device drivers, libraries, and applications for five different CPUs and three versions of WinCE. This particular file, is from the WinCE version group. It only provides information specific to WinCE 6.0. Other scripts provide version release file lists, overall packaging procedures, and CPU-specific information.

This resides in version control under $WinCe\Package\CE60 and is labelled by package release version. Related files are ProjList and CpuList.

This should not be invoked directly but only from CePack (note echo already is off. This is normally invoked from the BuildDir directory even though it and some files that it references are located in the Specific directory. Since BuildDir and Generic are at the same level, ..\Specific\ path doesn't fail whether CWD is BuildDir or Specific.

Environment variables inherited from CePack are CePackVer=CE60 and e.g. ReleaseVer=114, PackVer=CE60V114, GetVer=GetCE60.bat, PackName=EloCE60V114

This requires a release-labeled ProjList file, which contains pairs of component/file names and their versions for this release, e.g. (for V114)
EloHid 1.23
EloTch 1.20
EloStub 3.11
EloVa 2.20
EloCpl 3.13

This script does not embed the release version, which is human input via CePack. It does not know the project/components or their version numbers, which is provided by the release-labelled ProjList file. It does not know which target CPUs are support, which is provided by the release-labelled CpuList. However, it assumes that bib,pbpxml,reg files constitute the complete list of files that are needed for each project because this is CE6.0-specific. This script may never change but will be labelled for each release version so that it can change if necessary without disturbing previous releases.



LIST INSTALLED PROGRAMS

Tell installed programs by searching uninstall reg key and filtering on DisplayName (using grep). Arguments: None needed but if one is provided, another level of filtering is applied to it (by reinvoking grep). e.g. installed mcaf shows all installed McAfee programs. If an argument is not provided a second level of grep is still done to filter out the many Microsoft “update” items, which are rarely of any interest.

-w is used for grep to filter out instances of DisplayName that are embedded in other names e.g. ParentDisplayName, which appears in W764 Wow6432Node. Wow6432Node Uninstall is added for W764 for the 32-bit programs that are installed, e.g. 7-zip. This doesn't require a special version of the script; The key is queried unconditionally with reg errors directed to nul.

rem installed.bat
if _%1 == _? (
echo installed.bat lists installed programs by searching uninstall reg key
echo With no argument, all programs are listed. Otherwise, the argument is a filter
echo passed to grep -i. This can be a regular expression if it includes -E, e.g.
echo installed -E "(libre)|(7-zip)"
    goto done
)

if "%1" == "" (
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s | grep -w DisplayName | grep -v -i update
echo ------------------------------
reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" /s 2>nul | grep -w DisplayName | grep -v -i update
) else (
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s | grep -w DisplayName | grep -i %*
echo ------------------------------
reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" /s 2>nul | grep -w DisplayName | grep -i %* 
)
:done


SHOW INSTALLED PROGRAM DETAILS

See installed.awk

Show the DisplayName, Publisher, and InstallLocation of all installed programs or just those that contain the given filter string in one of these three values. Programs whose name contains “update” are ignored. Backslash in path filters must be replaced by / at the command. See installed.awk for additional details. Use installed.bat for listing and filtering on just the DisplayName.

Invocation Examples:
installedx
installedx libre
installedx d:/wpgm

Wow6432Node Uninstall is added for W764 for the 32-bit programs that are installed, e.g. 7-zip. This doesn't require a special version of the script. The key is queried unconditionally with reg errors directed to nul.

This passes the name of installed.awk to awk. A a full path is needed unless installed.awk is in %CD%, which is unlikely, given that this batch script is a general utility, which may be invoked from any directory. Instead of hard-wiring a path, it is assumed that the awk script will always be located in the same directory as the batch script, whose path is represented by %~dp0.

rem installedx.bat
if _%1 == _? (
echo installedx shows the DisplayName, Publisher, and InstallLocation of all 
echo installed programs or just those that contain the given filter string in 
echo one of these three values. Backslash in path filters must be replaced by
echo / in the command line.
    goto done
)

if "%1" == "" (
    reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s | awk -f %~dp0installed.awk
    echo ------------------------------
    reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" /s 2>nul | awk -f %~dp0installed.awk
) else (
    reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s | awk -v filter=%1 -f %~dp0installed.awk
    echo ------------------------------
    reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" /s 2>nul | awk -v filter=%1 -f %~dp0installed.awk
)
:done


SHOW LINK PROPERTIES


rem link.bat
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
if _%1 == _ (
:help
...
    goto done
)

if /i _%1 == _show ( 
    if _%2 == _ (
        for %%f in (*.lnk) do shortcut /F:"%%f" /A:Q 
        goto done
    ) 
    if /i _%2 == _target (
        set SearchFor=TargetPath=
    ) else if /i _%2 == _arguments (
        set SearchFor=Arguments=
    ) else if /i _%2 == _directory (
        set SearchFor=WorkingDirectory=
    ) else if /i _%2 == _icon (
        set SearchFor=IconLocation=
    ) else goto showOne
    for %%f in (*.lnk) do (
        echo -- In "%%f" see --
        shortcut /F:"%%f" /A:Q | grep !SearchFor!
    )
) else if /i _%1 == _find (
    if _%2 == _ (
        echo Command error: missing search string
        goto help
    )
    for %%f in (*.lnk) do ( 
        echo -- In [%%f] find --
        shortcut /F:"%%f" /A:Q | grep -i %2
    )
) else if /i _%1 == _FolderIcon (
    if "%LINKFOLDERICON%" == "" (
        shortcut /F:%2 /A:E /I:%%SystemRoot%%\system32\SHELL32.dll,155
    ) else ( shortcut /F:%2 /A:E /I:%LINKFOLDERICON% )
)

goto done

:showOne
if not exist %2 ( 
    echo Command error: '%2' does not exist
    goto help 
) else ( shortcut /F:%2 /A:Q )
goto done

:done
set /p dummy=Press Enter to close

link.bat displays attributes of one or all lnk files in CWD



COMMA-DELIMITED DIRECTORY


rem dirCsv.bat
dir %* /b | awk -f %~dp0toCsv.awk

dirCsv.bat shows files names in comma-delimited list. This can take arguments to dir, such as filter and /s, but not formatting switches. Use dirCsvQ.bat for quoted names.

rem dirCsvQ.bat
dir %* /b | awk -v Q=1 -f %~dp0toCsv.awk

dirCsvQ.bat shows quoted file names in comma-delimited list. This can take arguments to dir, such as filter and /s, but not formatting switches. Use dirCsv.bat for unquoted names.

# toCsv.awk
BEGIN { FS="\n" }
{ if( Q == 1 ) printf "\"%s\", ", $1 ; else printf "%s, ", $1 }

toCsv.awk prints the input string with trailing comma and space but not newline. Mainly used with dirCsv.bat to show all files in the current directory in a comma-delimited list. Each string is quoted if the command line variable Q is defined as 1, e.g. dir %* /b | awk -v Q=1 -f \CmdTools\toCsv.awk




Prev   Next   Site Map   Home   Top   Valid HTML   Valid CSS