<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Case Else: &#187; scripting</title>
	<atom:link href="http://caseelse.net/category/dev/script/feed/" rel="self" type="application/rss+xml" />
	<link>http://caseelse.net</link>
	<description>/*** Code's last stand ***/</description>
	<lastBuildDate>Sat, 29 Aug 2009 20:41:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Multithreading in Batch Script, Part 2: Running Code</title>
		<link>http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-2-running-code/</link>
		<comments>http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-2-running-code/#comments</comments>
		<pubDate>Thu, 22 May 2008 20:25:54 +0000</pubDate>
		<dc:creator>neil</dc:creator>
				<category><![CDATA[Batch Scripts]]></category>

		<guid isPermaLink="false">http://caseelse.net/?p=21</guid>
		<description><![CDATA[Wherein we take the multithread concept, and run like blazes. This article uses some of the files from Multithreading in Batch Script, Part 1: An Example; so before you get deep into it, grab the master.bat and computers.csv from there. This thread script: takes a given PC name, pings it and reverse-resolves it. logs it [...]]]></description>
			<content:encoded><![CDATA[<p>Wherein we take the multithread concept, and run like blazes.<br />
<span id="more-21"></span><br />
This article uses some of the files from <a href="http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-1-an-example/">Multithreading in Batch Script, Part 1: An Example</a>; so before you get deep into it, grab the master.bat and computers.csv from there.</p>
<p>This thread script:</p>
<ol>
<li>takes a given PC name, pings it and reverse-resolves it.</li>
<li>logs it as responsive or unresponsive (if unresponsive, it quits)</li>
<li>maps a drive to the PC, with the same letter as the thread</li>
<li>runs arbitrary code against the pc</li>
<li>unwinds</li>
<li>deletes itself</li>
</ol>
<p>The drive mapping can take a list of username/password combinations, starting with the user who runs it.</p>
<pre class="brush: text">@ECHO OFF
(set ip=%3)
(set pcname=%3)
(set drive=%2)
(set num=00000%1)
(set logfile=logs\%num%.txt)
(set outputname=thread)
(Set outputfile=.\log.csv)

:::::::::::::::::::::::::::::::::::::::::::::::
:: 1) Set variables above. This file called as;
:: newletter.bat    count newletter LineFromCSV
:: parameters are     %1      %2      %3,%4...
::
:: 2) Write a batch file that scans what you
:: need locally, and uses the variables above.
::
:: 3) Update the &quot;:::Variable Part:::&quot; section
:: with your batch file.
:::::::::::::::::::::::::::::::::::::::::::::::

ECHO. %*

set num=%num:~-5%
(SET pingnum=1)
set pingtarget=%pcname%
:PINGSTAT
@for /F &quot;delims=&quot; %%A in (&#039;ping -n %pingnum% -a %pingtarget%&#039;) do (
 call :log xxxLx &quot;%%A&quot;
 @for /F &quot;tokens=1,3,11&quot; %%B in (&quot;%%A&quot;) do (
  @IF &quot;%%B&quot;==&quot;Pinging&quot; (SET resolvedip=%%C)
  @IF &quot;%%B&quot;==&quot;Packets:&quot; (SET percentloss=%%D)
))
if DEFINED percentloss (SET percentloss=%percentloss:~1,-1%)
if DEFINED resolvedip (SET ip=%resolvedip:~1,-1%)

IF NOT &quot;%percentloss%&quot;==&quot;100&quot; GOTO :RESPONSIVE
:: quarter ping failed. Check for partial connectivity.
:: if full ping failed, exit.
call :log xxxLx &quot;%pcname% Failed ping [*%pingnum%]&quot;
@if &quot;%pingnum%&quot;==&quot;1&quot; (
 (SET pingnum=4)
 call :log xxxLx &quot;retrying with 4 pings . . .&quot;
 GOTO :PINGSTAT
)
call :log xxULC &quot;%num%,%pcname%,%ip%,Unavailable. Lost %percentloss% percent of %pingnum% pings&quot;
GOTO :END

:Responsive
call :log xxxLx &quot;%percentloss% percent Loss&quot;
IF &quot;&quot;==&quot;%percentloss%&quot; (
call :log xxULC &quot;%num%,%pcname%,%ip%,Not Found&quot;
GOTO :END
)

:: At this point, the machine is online and available

::::::::::::: connect drive :::::::::::::
if NOT exist %drive%:\nul GOTO ADDDRIVE
:DRIVEEXISTS
call :log xxxLx &quot;%drive% exists . . .&quot;
@for /F &quot;delims=&quot; %%A in (&#039;net use %drive%: /delete /y&#039;) do (call :log xxxLx &quot;%%A&quot;)
if exist %drive%:\nul (
 ping -n 3 localhost &gt;null
 goto :DRIVEEXISTS
)
call :log xxxLx &quot;%drive%:\nul doesn&#039;t exist&quot;

:ADDDRIVE
call :log xxxLx &quot;attaching \\%pcname%\c$ to %drive%:&quot;

FOR %%A in (&quot;%USERID% %PASSWORD%&quot;,&quot;%pcname%\Administrator Password&quot;) do (
  For /F &quot;tokens=1,2&quot; %%B in (%%A) do (
  call :connect %%B %%C
))
IF &quot;%ERR%&quot;==&quot;0&quot; goto :MAIN
call :log xxxLC &quot;%num%,%pcname%,%IP%,Can&#039;t Connect&quot;
Goto :End
::::::::::::: /connect drive :::::::::::::

:MAIN
call :log xxxLx &quot;Entering Main . . . &quot;
del netuse%drive%.txt -y

:::::::::::::::::: Variable part ::::::::::::::::::

:: Log success
call :log SxxLC &quot;%num%,%pcname%,%ip%

:::::::::::::::::: /Variable part ::::::::::::::::::
call :log xxxLx &quot;Disconnecting . . . &quot;
net use %drive%: /delete /y

GOTO :END

:log
:: SFUL (Success Failure Unavailable LogDetails CommonOutput) &quot;Message&quot;
(set log=%1)
echo %~2
if &quot;%log:~0,1%&quot;==&quot;S&quot; ECHO.%~2&gt;&gt;%outputname%.Updated.log
if &quot;%log:~1,1%&quot;==&quot;F&quot; ECHO.%~2&gt;&gt;%outputname%.Noupdate.log
if &quot;%log:~2,1%&quot;==&quot;U&quot; ECHO.%~2&gt;&gt;%outputname%.Unavail.log
if &quot;%log:~3,1%&quot;==&quot;L&quot; if defined logfile ECHO.%~2&gt;&gt;%logfile%
if &quot;%log:~4,1%&quot;==&quot;C&quot; if defined outputfile ECHO.%~2&gt;&gt;%outputfile%
Goto :EOF

:connect
if EXIST %drive%:\nul Goto :EOF
call Set USER=%1
call Set PWD=%2
call :log xxxLx &quot;connecting as %USER%&quot;
if &quot;%USER%&quot;==&quot;&quot; (
 net use %drive%: \\%pcname%\c$ &gt; netuse%drive%.txt 2&gt;&amp;1
) ELSE (
 net use %drive%: \\%pcname%\c$ /USER:%USER% %PWD% &gt; netuse%drive%.txt 2&gt;&amp;1
)
(set ERR=%ERRORLEVEL%)
call :log xxxLx &quot;netuse returns &#039;%ERR%&#039;&quot;
type netuse%drive%.txt
if defined logfile type netuse%drive%.txt&gt;&gt;%logfile%
IF &quot;%ERR%&quot;==&quot;0&quot; (
 call :log xxxLx &quot;Connected as %USER%.&quot;
 del netuse%drive%.txt -y
 goto :EOF
)
FOR /F &quot;tokens=3 skip=1&quot; %%p IN (&#039;Find /I &quot;System Error&quot; netuse%drive%.txt&#039;) DO (SET NETUSEERR=%%p)
call :log xxxLx &quot;NETUSEERR = %NETUSEERR%&quot;
FOR /F &quot;delims==&quot; %%p IN (&#039;net helpmsg %NETUSEERR%&#039;) DO (
SET NETUSEDESC=%%p
)
(SET NETUSEERR=[%NETUSEERR%] %NETUSEDESC:,=%)
del netuse%drive%.txt -y
call :log xxULx &quot;%num%,%pcname%,%ip%,Doesn&#039;t Connect as %USER%. &#039;%NETUSEERR%&#039;&quot;
Goto :EOF

:END
call :log xxxLx &quot;Done.&quot;
del %0
</pre>
<p><code><a href="http://caseelse.net/wp-content/uploads/2008/05/thread2.bat">download thread2.bat</a></code></p>
<p>This code does nothing to the target machines; it assumes that if it&#8217;s made it as far as a mapped drive, it&#8217;s finished. You can actually run just about anything against the machines: write a batch file to do *stuff* and use the variables listed in the block at the beginning of this script. When it works, plug it into this script, in the section marked
<pre>:::::::::::::::::: Variable part ::::::::::::::::::</pre>
<p> if you need to execute a process on the target machines, you can do so with psexec or beyondexec. You can with rclient, also, but it&#8217;s turning into a security liability, at this age.</p>
]]></content:encoded>
			<wfw:commentRss>http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-2-running-code/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Multithreading in Batch Script, Part 1: An Example</title>
		<link>http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-1-an-example/</link>
		<comments>http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-1-an-example/#comments</comments>
		<pubDate>Thu, 22 May 2008 18:33:18 +0000</pubDate>
		<dc:creator>neil</dc:creator>
				<category><![CDATA[Batch Scripts]]></category>

		<guid isPermaLink="false">http://caseelse.net/?p=18</guid>
		<description><![CDATA[Multithreaded applications have the potential to run much faster than single-threaded applications, given the right circumstances. With batch scripts, we often write routines that spend a lot of their time waiting for other things to finish; downloading sets of files, for example, or pinging a range of servers to see if they are alive. Blocking [...]]]></description>
			<content:encoded><![CDATA[<p>Multithreaded applications have the potential to run much faster than single-threaded applications, given the right circumstances. With batch scripts, we often write routines that spend a lot of their time waiting for other things to finish; downloading sets of files, for example, or pinging a range of servers to see if they are alive.<img class="size-full wp-image-19" style="float: left;padding-right:10;margin-left:10;margin-right:10;margin-top:0;display:inline;" title="Various Agent Smiths" src="http://caseelse.net/wp-content/uploads/2008/05/agentsmith.png" alt="Various Agent Smiths" width="160" height="100" /> Blocking functions (ie, where the scripts stalls until they are done) that spend their time waiting for other things are the big beneficiaries of multithreading. With some care, we can get the same benefits by &#8216;multithreading&#8217; our batch scripts.</p>
<p><span id="more-18"></span></p>
<p>What makes multithreading worthwhile is lag and the fact that &#8220;all computers wait at the same speed&#8221;. Without some sort of wait, threading can actually slow a system down due to overhead. Think of it like this: the pony express moved mail at the maximum speed a horse could travel. If they had implemented it with two or more horses, running parallel, the mail wouldn&#8217;t have gotten there any faster. two horses (ignoring load, so carriages don&#8217;t enter into this) don&#8217;t run faster than one horse. On the other hand, if running parallel horses doesn&#8217;t increase your speed, but you add the overhead of having to split the mailbags (ie, take the time to count or weigh the mail, so that the horses carry the same amount) then you are running at a net loss.</p>
<p>The pony express, however, was a system that was running at near maximum efficiency from our point of view. Adding more horses into the mix could increase complexity and overhead, but not speed up the system. It&#8217;s a different story for pizza delivery boys, or jugglers. Jugglers spend most of their time waiting for the ball to drop&#8211;literally. They throw a ball into the air, and it flies up, stops, falls back down. It isn&#8217;t until the ball comes back down that the juggler has any more input into the process. A juggler can therefore achieve higher efficiency by launching multiple <em>asynchonous</em> balls. They&#8217;re not parallel, per se, but they are fired off one after another, and function on their own. In this case, the balls can slightly change the &#8216;running&#8217; of each other. To increase the numbers of balls in the air, it becomes necessary to throw them higher, requiring that their &#8216;runtime&#8217; be slightly longer. In general, though, you can keep adding balls until you are running your juggler at maximum speed before you have to lengthen the &#8216;run&#8217; of the balls. Once your juggler is maxed out, it will affect the speed of the system, but you&#8217;ve still drastically increased the amount of <em>stuff</em> that&#8217;s going on by utilizing the juggler&#8217;s idle time.</p>
<p>Say a busy pizza company guarantees delivery within x minutes. In this case, using one delivery boy could get very expensive&#8211;any delivery x/2 minutes away takes up the delivery boy&#8217;s whole transit time (x/2 minutes each way). Any order placed while the delivery boy is out will be delayed while he is out, and could easily be delayed beyond the time limit. The company could try to maximize their efficiency by organizing deliveries together on a trip, and hiring the fastest possible driver to minimize the transit times&#8211;but the customer is a wildcard. Nobody knows how long it&#8217;s going to take a customer to check their order, look for their checkbook, find out checks aren&#8217;t accepted, look for cash, and work out a tip&#8211;and all pizza boys wait at the same speed. What they need to do is hire several delivery boys, have each one take one existing order and leave (in parallel) and as soon as they are back, take the next order and run (asynchronously). In the pizza business, they probably want to grab the pizza that needs to be delivered furthest, but they could always settle on the first-come, first-served model.</p>
<p>Multithreading requires a process to run, and a control system to keep it from running amok. Below we have the control system for a simple multithread emulator. It takes a pool of necessary tasks and keeps a list of available threads that it can use to process those tasks. Our model is most like the pizza company. We have arbitrary length processes, and are going to fire off as many as we can at once, and as each finishes, it grabs the next and runs. We don&#8217;t prioritize the machines, so they&#8217;ll just be first-come, first-serve.</p>
<p>Our &#8216;thread&#8217; process will need do several things, in order to function as an efficient system.</p>
<ol>
<li>it will quarter-ping the target, to make sure it is online</li>
<li>if the quarter ping succeeds, excellent, but on failure the thread will perform a full ping, to account for network connectivity issues</li>
<li>if the machine is found online, the thread will map a drive to that machine&#8217;s default admin shares</li>
<li>the tread will perform an arbitrary process against the target</li>
</ol>
<p>The first three actions all require waiting for the network or the target machine to respond. While waiting, the process will use around 0% of the CPU. This makes it a candidate for threading. If processing a single target machine took 100% of the CPU, then threading would take more resources than it returned. In our case, we can take advantage of unused cycles.</p>
<ul>
<li>We&#8217;re going to emulate the task pool with a list of machines that need the same task run on each of them.</li>
<li>We&#8217;ll also emulate the available threads with a list of available network drives. In a full application, we&#8217;ll be mapping a drive to each of the PCs, so using the available drives gives us both a control (we only run as many threads as we have drives, so we can change the number of threads we wish to run) and a mechanism to identify what is connected where. This has the side-effect of making this file specific to a computer. To make it universal, we would want to have it generate it&#8217;s own list based on what drives are not mapped in the system.</li>
</ul>
<p><!-- file="http://caseelse.net/wp-content/uploads/2008/05/master.bat" --></p>
<pre class="brush: text">@ECHO OFF
(set drivestring=J K L)&amp;(set childtemplate=thread.bat)&amp;(set inputfile=computers.csv)
Set outputfile=.\log.csv
Echo.number,machine,ip&gt; %outputfile%
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Set variables above.
:: Make sure drivestrings reflect executing machine&#039;s available drives.
:: If common outputfile is used, make sure it matches in %childtemplate%
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

set /P USERID=Enter your domain\userid:
set /P PASSWORD=Enter your password:
cls

@ECHO OFF
MD logs
set strlen=0
for %%A in (%drivestring%) do SET /A strlen+=1

SET count=0

FOR /F &quot;delims=&quot; %%A IN (%inputfile%) DO (
 call SET /A count+=1
 echo.
 call set comline=%%A
 call :processmachine %%A
)
pause
goto :EOF

:processmachine
  echo Processing #%count%: %comline%
  set newletter=
  @for %%D in (%drivestring%) do (
   @if NOT exist %%D.bat (
    @if NOT exist %%D:\nul (call set newletter=%%D)
    @if exist %%D:\nul Echo %%D: already connected. Skipping.
   )
  )
  if NOT DEFINED newletter (
   nul
   goto processmachine
  ) ELSE (
   @echo new letter = &#039;%newletter%&#039;
   type %childtemplate% &gt; %newletter%.bat
   START &quot;%count% -- %newletter%.bat&quot; %comspec% /c newletter%.bat %count% %newletter% %comline%
 )</pre>
<p><code><a href="http://caseelse.net/wp-content/uploads/2008/05/master.bat">download master.bat</a></code><br />
The master takes the list of available thread slots, and for each one, it makes a copy of the thread template named x.bat (where x=slot name), and launches that bat file. When it runs out of slots, it sleeps briefly, and looks through the list, to see if any of the bat files are missing&#8211;thereby using the bat file itself as a crude lock. As long as the file is running, and the file is there, that process &#8216;owns&#8217; that slot (and the corresponding drive mapping). The last thing a thread needs to do before it exits, is to delete itself.</p>
<p>This is weak as threaded systems go; it relies on the individual threads to clean up after themselves. If a thread batch dies, it leaves behind an orphaned lock file, and that thread doesn&#8217;t get reused&#8211;resulting in a resource leak. A better alternative would be to check the running process list for each thread-bat rather than relying on the existence of the lock.</p>
<p>All of this should fall well short of the mark for qualifying as a true &#8216;multithreaded&#8217; system; for the purpose of this discussion, I&#8217;m using the term &#8216;multithreading&#8217; loosely, vaguely, and expansively. I believe each command prompt executes in its&#8217; own processor thread (although that would be worth looking into); but that shouldn&#8217;t be enough to classify it as true multithreading. &#8220;Asynchronously executing multiple simultaneous batch processes&#8221; is probably a better description. This is, however, sufficient for quickly assembling network compliance scanners, application fixes, and tools of that nature.</p>
<p>The master runs against a list of PCs; for each machine, launching a thread, and passing it some pertinent information. To that end, we need a list. The master.bat is set up to read from a CSV file named computers.csv. For our purposes, all it needs to be is a list of PC Names, one per row:</p>
<pre class="brush: text">PCName1
PCName2
PCName3
PCName4
PCName5</pre>
<p><code><a href="http://caseelse.net/wp-content/uploads/2008/05/computers.csv">download computers.csv</a></code><br />
Below, you will find a simple thread template. This one is really just an illustration, so it&#8217;s short; in part two, we will look at a significantly more intricate version.</p>
<pre class="brush: text">@echo off
(set ip=%3)
(set pcname=%3)
(set drive=%2)
(set num=00000%1)

Title Thread %~n0 handling %PCNAME%

&lt;nul (set/p z=Running)
echo.

    for /l %%A in (%1,1,10) do (
        &lt;nul (set/p z=%%A )
        &gt;nul ping 127.0.0.1 -n 2
    )
echo.

Del %0</pre>
<p><code><a href="http://caseelse.net/wp-content/uploads/2008/05/thread.bat">download thread.bat</a></code><br />
This bat simply puts up a progress bar, then exits. Each bat runs for one second less than the previous one, beginning with 10 seconds. You can run the bat, if you like&#8211;it makes no changes on the machine&#8211;but realize that the last thing it does is delete itself. (Nothing will get you in the habit of backing up your work faster than working on self-deleting scripts.)</p>
<p>If you copy these three files down to a common directory and run them, it should all work. They&#8217;re set up to run five machines on three available threads, so you can get a good idea of how it works. When run, the master.bat will prompt for a username/password combination. This isn&#8217;t actually used in this iteration (it&#8217;s in there for <a href="http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-2-running-code/">Multithreading in Batch Script, Part 2: Running Code</a>). You can just enter through the prompts (<a href="http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-2-running-code/">Multithreading in Batch Script, Part 2: Running Code</a> is written so that if you enter through the prompts, it will use your current credentials.)</p>
]]></content:encoded>
			<wfw:commentRss>http://caseelse.net/2008/05/22/multithreading-in-batch-script-part-1-an-example/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Recursion in Batch, Part 2: a tree command</title>
		<link>http://caseelse.net/2008/05/21/recursion-in-batch-part-2-a-tree-command/</link>
		<comments>http://caseelse.net/2008/05/21/recursion-in-batch-part-2-a-tree-command/#comments</comments>
		<pubDate>Wed, 21 May 2008 05:58:02 +0000</pubDate>
		<dc:creator>neil</dc:creator>
				<category><![CDATA[Batch Scripts]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://caseelse.net/?p=15</guid>
		<description><![CDATA[FileTree is a rewrite of the Tree.com command. FileTree started off as a simple example of recursion, but turned out to be 2K long&#8211;so play with it if you wish. It turned out much better than I had anticipated; and it uses some Batch geek tricks to make things work. Treeing is the quintessential recursive [...]]]></description>
			<content:encoded><![CDATA[<p>FileTree is a rewrite of the Tree.com command.<a href="http://caseelse.net/wp-content/uploads/2008/05/recursionagain.jpg"><img class="size-medium wp-image-16" title="Motivational Poster for Recursion" src="http://caseelse.net/wp-content/uploads/2008/05/RecursionAgain250x200.png"  style='float: right; margin-left:10;margin-top:0; display:inline;' alt="Motivational Poster for Recursion" /></a> FileTree started off as a simple example of recursion, but turned out to be 2K long&#8211;so play with it if you wish. It turned out much better than I had anticipated; and it uses some Batch geek tricks to make things work. Treeing is the quintessential recursive *thing*. Without recursion, this sort of command would likely be impossible in Batch, and pretty damn hard in other languages.<br />
<span id="more-15"></span></p>
<p>Recursion is a simple concept once it gets into your head, and there are any number of treatises about it on the web&#8211;including <a href="http://caseelse.net/2008/05/21/recursion-in-batch-part-1-an-example/">Recursion in Batch, Part 1: An Example</a>&#8211;so go read that if you aren&#8217;t comfortable with it.</p>
<div class="boundingbox">
<div class="CMDPrompt">
<div class="CMDPromptTitle">C:\WINDOWS\System32\cmd.exe &#8211; filetree \windows\system32\wbem</div>
<div class="CMDPromptBox">
C:\test\Desktop\recurse>filetree \windows\system32\wbem<br />
<br />&nbsp;<br />
C:\WINDOWS\system32\wbem<br />
â•ž AutoRecover<br />
â•ž Logs<br />
â•ž mof<br />
â”‚  â•ž bad<br />
â”‚  â•˜ good<br />
â•ž Performance<br />
â•ž Repository<br />
â”‚  â•˜ FS<br />
â•˜ xml<br />
Press any key to continue . . .<br />
<br />&nbsp;
</div>
</div>
</div>
<p>To create a tree routine without recursion, you&#8217;d have to create large dynamic arrays, or something worse&#8211;I&#8217;ve had to do that in Wise (which has no array functionality) by appending folderspecs to a string, and keeping an eye on the length of the string, so I can shove overflow into another . . . it ain&#8217;t worth it.</p>
<pre class="brush: text">@ECHO OFF
IF [%1]==[/?] (
 Echo Graphically displays the folder structure of a drive or path.
 Echo Usage: %~nx0 [/dasf] [drive:][path] [ [/dasf] [drive:][path] ...]
 Echo   /d:  show File Dates
 Echo   /a:  Show File Attributes
 Echo   /s:  Show FileSizes
 Echo   /f:  Show Files in Directories
 Echo   /t:  Use Tree.com format
 Echo   /c:  Use compact format - negates /t
 Echo.
 GOTO :EOF
)

(SET Hasfiles=) &amp; (SET Root=)
Set Params=/
Goto :GotSwitch

:ParseParams
SHIFT
SET PARAMS=%~0
If &quot;%PARAMS%&quot;==&quot;&quot; (IF &quot;%ROOT%&quot;==&quot;&quot; (SET PARAMS=.) ELSE (GOTO :DONE))
IF &quot;%PARAMS:~0,1%&quot;==&quot;/&quot; GOTO :GOTSWITCH
IF &quot;%PARAMS:~0,1%&quot;==&quot;-&quot; GOTO :GOTSWITCH
SET Root=%PARAMS%
CALL :Root &quot;%ROOT%&quot;
GOTO :ParseParams
:GOTSWITCH
(SET COMPACT=) &amp; (SET SHOWDATE=) &amp; (SET SHOWATTRIB=) &amp; (SET SHOWSIZES=) &amp; (SET SHOWFILES=) &amp; (SET TREESTYLE=)
:NEXTSWITCH
SET PARAM=%PARAMS:~0,1%
SET PARAMS=%PARAMS:~1%
IF /I [%PARAM%]==[d] SET SHOWDATE=True
IF /I [%PARAM%]==[a] SET SHOWATTRIB=True
IF /I [%PARAM%]==[s] SET SHOWSIZES=True
IF /I [%PARAM%]==[f] SET SHOWFILES=True
IF /I [%PARAM%]==[t] SET TREESTYLE=True
IF /I [%PARAM%]==[c] SET COMPACT=True
IF NOT [%PARAMS%]==[] GOTO :NEXTSWITCH
IF /I [%COMPACT%]==[True] (SET TREESTYLE=)
GOTO :ParseParams

:Done
pause
GOTO :EOF
:ROOT
ECHO.
REM ~ ECHO Showdate=%Showdate%
REM ~ ECHO SHOWATTRIB=%SHOWATTRIB%
REM ~ ECHO SHOWSIZES=%SHOWSIZES%
REM ~ ECHO SHOWFILES=%SHOWFILES%
REM ~ ECHO FileSing=%FileSing%
REM ~ ECHO FileMult=%FileMult%
REM ~ ECHO DirSing=%DirSing%
REM ~ ECHO DirMult=%DirMult%
REM ~ goto :EOF

ECHO %~f1
:WalkTree
SETLOCAL
:: Set up Remainder of switches -- ME DirSpec |[|...] FileSpec
REM ~ Echo Params: %*
IF NOT [%1]==[] PUSHD %1
SET RECURPIPE=%~2

(SET LASTDIR=) &amp; (SET LASTFILE=)
For /D %%D in (*.*) DO SET LASTDIR=%%D
FOR %%F in (*.*) DO SET LASTFILE=%%F
REM ~ Echo LastFile=%LASTFILE%
REM ~ Echo LastDir =%LASTDIR%
REM ~ ECHO RecursivePipe %RECURPIPE% Multi %FILEMULT% single %FILESING%
IF /I [%TREESTYLE%]==[True] (
 (Set VPAD=True) &amp; (SET PADDING=) &amp; (SET SPC=   ) &amp; (SET FileSing=) &amp; (SET FileMult=) &amp; (SET DirSing=Ã€Ã„Ã„Ã„) &amp; (SET DirMult=ÃƒÃ„Ã„Ã„) &amp; (SET DIRPAD=Â®DIRÂ¯   )
) ELSE (
 (Set VPAD=True) &amp; (SET PADDING= ) &amp; (SET SPC=  ) &amp; (SET FileSing=Ã€) &amp; (SET FileMult=Ãƒ) &amp; (SET DirSing=Ã”) &amp; (SET DirMult=Ã†) &amp; (SET PipeCHR=Â³) &amp; (SET DIRPAD=Â®DIRÂ¯   )
)
IF /I [%COMPACT%]==[True] (Set VPAD=) &amp; (SET PADDING=) &amp; (SET SPC=) &amp; (SET FileSing=Ã€) &amp; (SET FileMult=Ãƒ) &amp; (SET DirSing=Ã”) &amp; (SET DirMult=Ã†) &amp; (SET PipeCHR=Â³) &amp; (SET DIRPAD=ÃÃÃÃÃ   )
IF /I NOT [%SHOWFILES%]==[True] (Set VPAD=)

IF &quot;%TREESTYLE%&quot;==&quot;True&quot; (
 IF &quot;%LASTDIR%&quot;==&quot;&quot; (
  (SET Pipe=%RECURPIPE%%FILEMULT%%SPC%) &amp; (SET Pipend=%RECURPIPE%%FILESING%%SPC%) &amp; (SET VPADDING=%RECURPIPE%)
 ) ELSE (
  (SET Pipe=%RECURPIPE%%PIPECHR%%SPC%) &amp; (SET Pipend=%RECURPIPE%%PIPECHR%%SPC%) &amp; (SET VPADDING=%RECURPIPE%%PIPECHR%)
 )
) ELSE (
 IF &quot;%LASTDIR%&quot;==&quot;&quot; (
  (SET Pipe=%RECURPIPE%%FILEMULT%) &amp; (SET Pipend=%RECURPIPE%%FILESING%) &amp; (SET VPADDING=%RECURPIPE%)
 ) ELSE (
  (SET Pipe=%RECURPIPE%%FILEMULT%) &amp; (SET Pipend=%RECURPIPE%%FILEMULT%) &amp; (SET VPADDING=%RECURPIPE%%PIPECHR%)
 )
)
IF [%3]==[] (SET Pipend=%PIPE%)

IF /I [%SHOWFILES%]==[True] FOR %%F in (*.*) DO (
 IF &quot;%LASTFILE%&quot;==&quot;%%F&quot; (CALL :PrintFile &quot;%PIPEND%&quot; &quot;%%F&quot;) ELSE (CALL :PrintFile &quot;%PIPE%&quot; &quot;%%F&quot;)
)

IF NOT &quot;%LASTFILE%&quot;==&quot;&quot; IF /I [%VPAD%]==[True] ECHO.%VPADDING%

(SET Pipe=%RECURPIPE%%DIRMULT%) &amp; (Set Pipend=%RECURPIPE%%DIRSING%)
(SET Pipecarry= ) &amp; (SET LASTFILE=)

For /D %%D in (*.*) DO if &quot;%LASTDIR%&quot;==&quot;%%D&quot; (
 CALL :PrintFile &quot;%PIPEND%&quot; &quot;%%D&quot;
 CALL :WalkTree &quot;%%D&quot; &quot;%RECURPIPE%%PIPECARRY%%SPC%&quot; &quot;%LASTDIR%&quot;
) ELSE (
 CALL :PrintFile &quot;%PIPE%&quot; &quot;%%D&quot;
 CALL :WalkTree &quot;%%D&quot; &quot;%RECURPIPE%%PIPECHR%%SPC%&quot; &quot;%LASTDIR%&quot;
)

POPD
ENDLOCAL
GOTO :EOF

:PrintFile
(SET FILESIZE=) &amp; (Set ATTRIBS=)
(Set ATTRIBS=%~a2 )
IF [%SHOWDATE%]==[True] (Set DATTIME=%~t2 )
(Set FILESIZE=%~z2       )
SET FILESIZE=%FILESIZE:~0,7%
IF 999999 LSS %~z2 (Set FILESIZE=%~z2)
(Set FILESIZE=%FILESIZE% )
IF /I &quot;%ATTRIBS:~0,1%&quot;==&quot;d&quot; (SET FILESIZE=%DIRPAD%)
IF NOT [%SHOWSIZES%]==[True] (Set FILESIZE=)
IF NOT [%SHOWATTRIB%]==[True] (Set ATTRIBS=)
:: Files with illegal characters need to stay quoted
SET FNAME=%2
SET FNAME=%FNAME:&amp;=%
:: Filenames with parentheses break IF blocks
IF [%FNAME%]==[%2] GOTO :DropQuotes
SET FNAME=%2
Goto :Print
:DropQuotes
SET FNAME=%~2
:Print
ECHO %~1%PADDING%%DATTIME%%ATTRIBS%%FILESIZE%%FNAME%
</pre>
<p><code><a href='http://caseelse.net/wp-content/uploads/2008/05/filetree.bat'>download filetree.bat</a></code><br />
<em>Originally written July 4th, 2005</em><br />
<em>Picture courtesy of <a href="http://bighugelabs.com/flickr/motivator.php">The Motivator</a></em></p>
]]></content:encoded>
			<wfw:commentRss>http://caseelse.net/2008/05/21/recursion-in-batch-part-2-a-tree-command/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Recursion in Batch, Part 1: An Example</title>
		<link>http://caseelse.net/2008/05/21/recursion-in-batch-part-1-an-example/</link>
		<comments>http://caseelse.net/2008/05/21/recursion-in-batch-part-1-an-example/#comments</comments>
		<pubDate>Wed, 21 May 2008 04:42:27 +0000</pubDate>
		<dc:creator>neil</dc:creator>
				<category><![CDATA[Batch Scripts]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://caseelse.net/?p=14</guid>
		<description><![CDATA[Recursion is one of those epiphanous things; people don&#8217;t seem to grasp it at first, but once you do; it can streamline a lot of problems. I started jotting down an illustration of recursion for a coworker, and it got complicated, and then it turned into an application, so you get two. Both are batch [...]]]></description>
			<content:encoded><![CDATA[<p>Recursion is one of those epiphanous things; people don&#8217;t seem to grasp it at first, but once you do; it can streamline a lot of problems. I started jotting down an illustration of recursion for a coworker, and it got complicated, and then it turned into an application, so you get two. Both are batch files (don&#8217;t ask . .. it was just for sport)</p>
<p><span id="more-14"></span><br />
Recursions can do their work inbound or outbound (there&#8217;s probably a term for it, but I don&#8217;t know what it is). In a similar way, if I want to know how far it is to your house, I can drive there, and clock the miles, or drive there then clock the miles on the return trip. The decision is made on details such as whether or not I know where your house is.</p>
<pre class="brush: text">@ECHO OFF
IF &quot;%1&quot;==&quot;&quot; (
 ECHO Usage: ! [1-12]
 GOTO :EOF
)

:: For any given recursion, you have to have an end,
:: where it begins to return. This is the &#039;bottom&#039; of
:: the recursion (similar to a do-while-loop)
IF &quot;%1&quot;==&quot;0&quot; (
 SET TOTAL=1
 Echo. %INDENT% ==========
 ECHO. %INDENT%  seed = 1
 Goto :EOF
)

:: Descending. Some recursions work on the way down,
:: some on the way up. This one only counts, on the way
:: down - partly because it&#039;s easier to seed The total
:: with 1 at that point, than try to create a mechanism to
:: store the number you enter, but not store any of the
:: other numbers on the way down. From the side, a
:: recursion is similar to dropping objects as you walk
:: through a maze, and picking them back up as you
:: retrace your steps.

:: Make this visual
Echo. %INDENT%Ãš Count = %1
:: /a = do arithmetic
Set /a COUNT=%1-1

:: Build connecting lines, as we go
(SET INDENT=%~2Â³)

:: Recurse - the function calls itself for the next step.
CALL !.bat %COUNT% &quot;%INDENT%&quot;

:: Ascending. As each function call returns, it daisy-chains
:: back up the way it descended, doing the last half of
:: each function call as it goes.

:: This is the single functional line of the function.
Set /a TOTAL=%TOTAL% * %1

:: indent=Mid$(indent,2)
SET INDENT=%INDENT:~1%
:: Make it visual
Echo. %INDENT%Ã€ * %1 = %TOTAL%</pre>
<p><code><a href='http://caseelse.net/wp-content/uploads/2008/05/%21.bat'>download !.bat</a></code><br />
!.bat calculates factorials. If you&#8217;ve forgotten, a factorial is a number multiplied by every positive integer smaller than itself: factorial X=1*2*&#8230;*X so factorial 3 (or 3!) = 3 * 2 * 1. This is an ideal case for recursive functions, because a) the series clearly ends, and b) for every value of x, that part of the function is x*x-1 or f(x)=x*f(x-1); so, if x=3, then 3! = (3 * 2!) where 2! = 2 * 1! and 1!=1. In other words, we don&#8217;t have to know anything more about the values other than for any x, x!=x*(x-1)!, and that 1!=1 (ie, that&#8217;s where we stop going deeper).</p>
<p>Run !.bat, and give it a number (ie, ! 3) . . . but 12 is it&#8217;s limit, because it&#8217;s doing 16-bit integer math. Beyond 65535 it rolls over like a pinball machine, and the math is just wrong. The script is simple, and you should get the hang of the code pretty quick. Heck, it&#8217;s only 40 lines, and most of them are comments, or are there to show the nesting as obviously as possible. In a real language, a lot of recursions are one or two lines long.</p>
<div class="boundingbox">
<div class="CMDPrompt">
<div class="CMDPromptTitle">C:\WINDOWS\system32\cmd.exe</div>
<div class="CMDPromptBox">
<p>C:\Documents and Settings\Administrator\Desktop\Blog\recurse&gt;! 3</p>
<p>â”Œ Count = 3<br />
â”‚â”Œ Count = 2<br />
â”‚â”‚â”Œ Count = 1<br />
â”‚â”‚â”‚ ==========<br />
â”‚â”‚â”‚  seed = 1<br />
â”‚â”‚â”” * 1 = 1<br />
â”‚â”” * 2 = 2<br />
â”” * 3 = 6</p>
<p>C:\Documents and Settings\Administrator\Desktop\Blog\recurse&gt;
</p></div>
</div>
</div>
<p>In the code, I embedded Unicode characters into the bat file. They look different in most other code pages, so you will probably see the following characters:</p>
<ul>
<li>Ãš = â”Œ</li>
<li>Â³ = â”‚</li>
<li>Ã€ = â””</li>
</ul>
<p>As you may guess, those three characters form the basis for the tree structure.</p>
<p>As a sidebar; this little app has an overflow condition, as mentioned above. It does 16-bit math, so as the total grows, it approaches a limit where it is no longer valid. The function grows (logarithmically? it&#8217;s not exponentially) faster as it goes, but if we used a more linear function (say, f(x)=x+1 ) we would still have constraints we need to worry about. Every time you call a bat file, the system opens a new shell space for it, with its&#8217; own environment full of it&#8217;s own copy of the existing environment variables. This environment is between 160 bytes and 32Kb, depending on <a href="http://www.robvanderwoude.com/command.html">how your system is set up</a>, and all of the running shells run in the same 640Kb section of memory reserved for DOS. If you run too many shells, they&#8217;re going to fill up the reserved space, have no where to put their children, and die of overpopulation (insert planned parenthood reference, here.) In higher level (or lower level) languages, this is analogous to &#8216;bashing <a href="http://en.wikipedia.org/wiki/Call_stack">the stack</a>&#8216;. Because of this, it&#8217;s important to evaluate things like how big it&#8217;s going to get. Our batch file can&#8217;t process numbers larger than 12, so it&#8217;s constraint keeps it fairly small; however your implementation of a <a href="http://99-bottles-of-beer.net/">google-bottles-of-beer-on-the-wall</a> probably wouldn&#8217;t get very far. In a case like that, putting your lyric into a function and calling it from a For loop would get you farther, because the function would be called one-at-a-time, instead of one-from-the-previous. In other words, each function call would end (and release its&#8217; environment/stack space) before the next one was called.</p>
<p>The second app in the series is <a title="Recursion in Batch, Part 2: a tree command" href="http://caseelse.net/2008/05/21/recursion-in-batch-part-2-a-tree-command/">Recursion in Batch, Part 2: a tree command</a>.</p>
<p><em>Originally written July 4th, 2005</em></p>
]]></content:encoded>
			<wfw:commentRss>http://caseelse.net/2008/05/21/recursion-in-batch-part-1-an-example/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>How to close open files on a server share</title>
		<link>http://caseelse.net/2008/05/17/how-to-close-open-files-on-a-server-share/</link>
		<comments>http://caseelse.net/2008/05/17/how-to-close-open-files-on-a-server-share/#comments</comments>
		<pubDate>Sat, 17 May 2008 02:54:35 +0000</pubDate>
		<dc:creator>neil</dc:creator>
				<category><![CDATA[Batch Scripts]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[scripting]]></category>
		<category><![CDATA[admin]]></category>
		<category><![CDATA[batch]]></category>

		<guid isPermaLink="false">http://neilbryant.net/articles/?p=3</guid>
		<description><![CDATA[One of our departments needed to upgrade software on a number of their servers, but it is an application that users like to leave running when they leave at night, and the connected files keep the upgrade from working. They needed to close thousands of open files in the application shares on several hundred servers. [...]]]></description>
			<content:encoded><![CDATA[<p>One of our departments needed to upgrade software on a number of their servers, but it is an application that users like to leave running when they leave at night, and the connected files keep the upgrade from working. They needed to close thousands of open files in the application shares on several hundred servers.</p>
<p>They asked me if there&#8217;s a way to close all of the connected files in the application shares on those servers. I put together this little batch script to do just that.<br />
<span id="more-5"></span></p>
<pre class="brush: text">@echo off
(set share=d:\share\)
REM !! IMPORTANT set length in 4th from last line !!

FOR /f &quot;Skip=4 tokens=1,2&quot; %%i in (&#039;NET FILES&#039;) do (
 IF /I NOT &quot;%%i&quot;==&quot;The&quot; (
  call :checkpath %%i &quot;%%j&quot;
 )
)
::pause
GOTO :EOF

:checkpath
 set id=%1
 set folder=%~2\
 :: !! The last number in the next line needs to
 :: be the length of the SHARE string
 SET root=%FOLDER:~0,9%
 IF /I NOT &quot;%ROOT%&quot;==&quot;%SHARE%&quot; GOTO :EOF
 echo %ID% is open on %FOLDER%. Closing...
 NET FILE %id% /CLOSE</pre>
<p>This was run directly from the package that  pushes the installer, but it could just as easily be pushed in any other manner. You could also use it on a schedule to drop open locks. You just need to uncomment the <code>pause</code> if you want to see the results. (the <code>::</code> is the equivalent of <code>REM</code> in batch, except it actually runs faster.)</p>
]]></content:encoded>
			<wfw:commentRss>http://caseelse.net/2008/05/17/how-to-close-open-files-on-a-server-share/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Timezones from the Commandline</title>
		<link>http://caseelse.net/2008/05/16/timezones-from-the-commandline/</link>
		<comments>http://caseelse.net/2008/05/16/timezones-from-the-commandline/#comments</comments>
		<pubDate>Fri, 16 May 2008 19:22:37 +0000</pubDate>
		<dc:creator>neil</dc:creator>
				<category><![CDATA[Batch Scripts]]></category>

		<guid isPermaLink="false">http://caseelse.net/?p=20</guid>
		<description><![CDATA[Timezones need to be set, sometimes. We move machines all over the country, and they need to be right when they get there. We&#8217;ve used various scripts in various languages to set the time and the timezones, but there is also a hard-to-find command line that will accomplish the task. control.exe timedate.cpl,,/Z Eastern Standard Time [...]]]></description>
			<content:encoded><![CDATA[<p>Timezones need to be set, sometimes. We move machines all over the country, and they need to be right when they get there. We&#8217;ve used various scripts in various languages to set the time and the timezones, but there is also a hard-to-find command line that will accomplish the task.<br />
<span id="more-20"></span></p>
<pre class="brush: text">control.exe timedate.cpl,,/Z Eastern Standard Time</pre>
<p>The time zone string is exactly the same as the one listed in the timezone applet. You can see a list of them in the registry under this key:</p>
<pre class="brush: text">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones</pre>
<p>Each timezone listed has a pair of subkeys names Dlt and Std, which are the strings for Standard time and Daylight Savings Time in that zone.</p>
]]></content:encoded>
			<wfw:commentRss>http://caseelse.net/2008/05/16/timezones-from-the-commandline/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

