Sometimes you gots to script windows. If it’s my personal rig I usually just use Cygwin because that’s where all the tools I need reside on Windows boxen. Either that or I just hack in Python which became my replacement for Perl after I went back and tried to read a 3 year old Perl script that broke. I know that code clarity depends on a programmer – and I’m very good at being sloppy in every language I know, but my shitty Python code is marginally more readable than my shitty Perl from that period in my life when I decided I’m really good at regular expressions.
But this is not about scripting for myself. This is about writing scripts that could possibly work on some limited range of machines that won’t have Perl, Python or Cygwin installed because they are operated by functional halfwits. More or less, the typical use case works like this – and end user walks in with a computer in tow and goes:
“Yo, my shit is all retarded.”
At that point your job is to un-retard his shit, whatever that might mean. Actually, what it usually means is that they changed, deleted or misplaced something. The usual procedure is to make them download and run bunch of installers, reset their home pages and re-jiggle their thingymabobs. This could be done by hand, but it is usually tedious. I have already created a tool that does a lot of such tedious work for me. While said tool became an indispensable asset for me, I try to keep it a generalized, all purpose tool – a Swiss Army Knife of sorts. I needed a set of specialized scripts that would parse, change, delete, download and run files to do some very specific tasks. Tasks that may periodically change – where periodically is defined as “more often than I would want to compile the damn code”.
Most of the time, you would probably do this sort of shit in VBScript. Before Powershell was a thing, VBScript was the go-to scripting language on Windows. It still is, seeing how it is not installed by default on Windows XP which is still on roughly half of the machines I have to deal with. The problem with VBScript is that it is a shitty language – and a verbose one too.
Let me give you an example – when I’m on Linux, Unix, Cygwin or a Mac, and I need to download a file from the interwebs, all I need to do is:
wget http://example.com/somefile.zip
In VBScript this is slightly more complicated:
URL="http://example.com/somefile.zip"
saveTo = "c:\some\folder\somefile.zip"
Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")
objXMLHTTP.open "GET", URL, false
objXMLHTTP.send()
If objXMLHTTP.Status = 200 Then
Set objADOStream = CreateObject("ADODB.Stream")
objADOStream.Open
objADOStream.Type = 1 'adTypeBinary
objADOStream.Write objXMLHTTP.ResponseBody
objADOStream.Position = 0 'Set the stream position to the start
Set objFSO = Createobject("Scripting.FileSystemObject")
If objFSO.Fileexists(saveTo) Then objFSO.DeleteFile saveTo
Set objFSO = Nothing
objADOStream.SaveToFile saveTo
objADOStream.Close
Set objADOStream = Nothing
End if
Or something along these lines. I actually did not test this – I shamelessly lifted the code from here. I just don’t care enough to actually make sure it’s correct – if it’s wrong, then it’s wrong. Don’t use that code. I guess what I’m saying here is that VBS is a shitty general purpose programming language that can be used for scripting, but it ain’t pretty. It was designed by people and for people who thought that Visual Basic was a good idea and it shows. Unix shell on the other hand is an elegant command line environment with a smorgasbord of nifty tools that work beautifully out of the box. Tools that are self contained, mature, tested and follow the unix philosophy of doing just one thing, but doing it well.
While the script above may poorly imitate a fraction of functionality of wget, but is flimsy, ugly and pain in the ass to maintain. It may solve one problem (downloading files from the internet) but wget is not the only tool I would like to use on a daily basis. There is abut a dozen of other GNU utilities that I wold like to have on Windows: sed, grep, diff, patch, head, tail, touch – just to name a few. All extremely useful, all nontrivial to re-create functionality-wise in VBScript.
For example – why spend an hour fiddling with VBS string processing functions and end up with about a 100 lines of unspeakably ugly code (90% of which is boilerplate and padding) if you could write 4 regular expressions and feed them to sed to accomplish the same thing. Granted, regexps are unspeakably ugly in themselves most of the time, but it’s still 90% less ugly per volume if you think about it.
The standard windows scripting environment (cmd.exe) is less verbose and more like unix shell in some aspects. It’s unfortunate that it is hampered by it’s syntax, and a very limited set of utilites. Powershell is much better in this aspect, but it is both more verbose and vb like and not as ubiquitous.
If you could only somehow “borrow” bunch of GNU shell utilities and bundle them with your standard Windows batch scripts, you could actually have quite a powerful tool at your heads. And I’m not talking about Cygwin. Yes, it is nice but often you don’t want the whole kit and caboodle – a separate shell with it’s own set of environmental variables, it’s own filesystem hierarchy is an overkill for a lot of task. Ideally you’d just want cherry pick select utilities – for example, if your script only needs sed and wget, then you would only include these.
Some time ago, I have discovered an old, but still somewhat relevant project called Unix Utils. It’s aim is basically to create dependency free Windows ports of all the core Unix utilities. The package ships with a rudimentary shell (sh.exe) but the tools in the usr/local/wbin are actually completely portable. You can extract the entire package, take out wget.exe, drop it in the same directory as your batch script and it will work.
The downside of this method is that it creates dependencies for your script. If you distribute it via email, you need to include all the external GNU executables with it. This is a problem, since your average office drone can’t be trusted to properly extract a zip file. I tried – on average my users failed to unpack such a bundle 13 times out of 10. No it’s not a typo – that’s just how hard they failed.
Alas, there is a tool that can help with that. It’s called WinRar and everyone loves it. I know, because I once made a poll and WinRar kinda won. WinRar is a neat compression tool, but it also has the ability to build so called SFX archives – self extracting bundles that can be instructed to run a program when they unravel themselves. You can do that directly from a GUI but it is tedious – a lot of clicking is involved. If you will be building and re-building your batch scripts a lot (and you will) you want something you can automate. Fortunately everything you need is in the WinRar program directory:
- rar.exe – is a stand alone command line version of WinRar
- Default.SFX – is a binary header that gets appended to the self extracting archives
You can grab those two files from the WinRar program directory and put them wherever. As long as both are in the same directory you don’t even need WinRar installed on the machine where you will be building the SFX bundle.
Next step is to create a config file sfx.conf where you specify where and how the bundle is to self extract. Here is an example:
Path=%TEMP%
Setup=%TEMP%\somedir\batchscript.cmd
Silent=1
Overwrite=1
Quick explanations:
- Path – is the directory where you want to extract your shit. I’m using the temp directory.
- Setup – is the program to be run after successful extraction. Note that I’m assuming that the bundle will extract to a sub-directory called somedir.
- Silent – setting this to 1 suppresses the GUI extraction dialog
- Overwrite – ensures that old files get overwritten as they are extracted
Now, you put you batch script and all the things you want to bundle with it in somedir\. Outside you put rar.exe, Default.sfx and sfx.conf. Once everything is in place, you run this command (or, you know – make it a script):
rar a -r -sfx -z"sfx.conf" setup.exe somedir\
Boom, now you have an executable called setup.exe which will quietly self-extract to temp dir, and run your batch script allowing it to call any and all binaries you included with it.
You want a practical example where this might be useful? Here is a script that changes the home page in Chrome. Changing IE homepage is somewhat trivial – it requires a simple registry hack. Changing it in Chrome, after it was already installed and configured is a tiny bit more complex. Essentially you need to parse the users’ Preferences file and change two values in it. This can be done in a number of ways, but being a unix geek I opted for something like this:
@echo off
set ppath=%USERPROFILE%\Local Settings\Application Data\Google\Chrome\User Data\Default
sed -n -f chrome_homepage.sed "ppath%\Preferences" > "%ppath%\Preferences.txt"
del "%ppath%\Preferences"
move "%ppath%\Preferences.txt" "%ffile%\Preferences"
Here is the Sed script that does the actual work:
s#homepage\": \"[^\"]*#homepage\": \"http://example.com#
s#homepage_is_newtabpage\": true#homepage_is_newtabpage\": false#
If you are having trouble reading it it’s because I’m using hash-marks (#) instead of forward slashers as regexp delimiters to avoid the zigzagging pattern of escaped slashes that usually accompanies regexps that deal with URL’s.
My SFX archive then includes 3 files – the batch script, the sed script, and the sed.exe executable from UnixUtils project. The user gets a bundled executable that will briefly flash a command line window for a split second, and his home page will be auto-magically reset to the proper value.
Is this the best possible way of doing this? No, probably not. It’s rather unorthodox, and old time Windows admins will probably yell at me for doing this. But it works, and it does let me accomplish a lot of complex tasks using good old Unix functionality without having to bang my head against the wall debugging VBS code, or forcefully install Powershell on WinXP machines.
I like regexes very much and usually sed is convenient, but if you need to do inplace editing I’d rather prefer perl -i -p -e ‘…’ or the old ex editor
ex -s \
-c "set ic|%s/User nobody/User ${httpRunUser}/g|wq" \
${ihsConfigDir}/${PROFILE}_httpd.conf
this example will replace the user in a httpd.conf inplace.
Isn’t JScript also available as a replacement for VBScript? It probably wouldn’t help much, as you’d have to use the same objects for doing stuff, but it would at least support regular expressions.
Thanks, this is a clever idea, so I’ve added it to my virtual toolbelt (mentally). Though I’ll use a self-extracting zip rather than rar.
@ ths:
Yeah, I could. I’m actually not sure if UnixUtils has ex in their package, but chances are they do. :)
@ Luchs:
Yeah, JScript is an option but it is similarly verbose.
@ Chris Wellons:
Well, the self-extracting bit turns it into an exe anyway so it doesn’t matter – user does not need zip or rar installed for it to work. I believe 7zip has the exact same functionality. :)
Sadly, UnixUtils doesn’t have ex in their package (I just checked). Still very useful tho.
I dunno if you’ve checked that poll lately, but from what I can see, WinRAR is no longer the winner.
You could also try Gow (Gnu On Windows) which basically is a one-click installer that will install all dependencies, take care of PATH, etc.
You might find it more bloated (it even includes Vim for the cmd console!) but it’s still under 6 MB…
@ STop:
Nice! I didn’t know this existed. Will have to check it out. Thank you sir!
If by users you mean the people who’s machines you manage for work, why not include the tools as part of a ‘standard company install’, thou the click and run works well for some people.
Ideally you would have ssh and cygwin on all the machines, and you could ‘magically’ fix everything, bar network issues