Looking for an app or script to find, rename and replace files.
Tuesday, March 16th, 2010 - 10:11am
I know this can easily be done in Linux with various commands but I am unsure how to do this in Windows. I looked at Sysinternals first and they don't seem to have what I want. I need to search for a file, by name, rename it to filename.extension.old and replace it with a new version in another directory. I did some reading on .bat, Powershell and Windows Scripting but I feel way over my head in this. Are any of you familiar with an easy solution or know of an existing one that you have used in the past?



If you know how to do it under Linux (or can easily find out) then there's always Cygwin.
Unofficial GWJ IRC Channel -- Stay awhile and listen
GWJ Starcraft 2 Ladder
Yeah just use Cygwin to run the Unix commands under Windows. I do it all the time with grep.
Other people have given you a solution but if you do want to get into .bat then it's actually pretty easy.
Is the file in a fixed location? Then the job is even that much more easy. Keep in mind all a batch file (I prefer the .cmd extension myself but it is the same thing) does is string together commands you'd type into the command line/window.
Here is a sample batch file:
You can also get fancy and copy the file from another computer so you don't have to copy it over to the computer first:
Keep in mind if there are spaces in your path names you will need to use " to define your paths, like so "C:\Program Files\Test\". However, certain directories in Windows have builtin shorter names for variables in batch files:
(These are for XP they may not work on other OS's the same way)
%ALLUSERSPROFILE% C:\Documents and Settings\All Users
%APPDATA% C:\Documents and Settings\{username}\Application Data
%COMPUTERNAME% {computername}
%COMSPEC% C:\Windows\System32\cmd.exe
%HOMEDRIVE% C:
%HOMEPATH% \Documents and Settings\{username}
%PATH% C:\Windows\System32\;C:\Windows\;C:\Windows\System32\Wbem
%PATHEXT% .COM; .EXE; .BAT; .CMD; .VBS; .VBE; .JS ; .WSF; .WSH
%PROGRAMFILES% Directory containing program files, usually C:\Program Files
%PROMPT% Code for current command prompt format. Code is usually $P$G
%SYSTEMDRIVE% The drive containing the Windows XP root directory, usually C:
%SYSTEMROOT% The Windows XP root directory, usually C:\Windows
%TEMP% and %TMP% C:\DOCUME~1\{username}\LOCALS~1\Temp
%USERNAME% {username}
%USERPROFILE% C:\Documents and Settings\{username}
%WINDIR% C:\Windows
There is a way to do this from your machine on many remote machines with batch and using psexec from sysinternals but learn to walk first.
Do you ever walk alone like a drifter in the dark?
---------------------------------------------------------------
Steam: Bordone
All right, here is my jack of all trades psexec script(s). You can modify this to your hearts desire but one of the best way to learn to script is to see others. So here goes. NOTE - All quoted text should be one line, some lines wrapped.
This script can be tailored to do just about anything, it is a script that calls another script BUT with the important distinction that that other script is then running on another computer. I've rolled out the Win2K/2K3 DST changes to over 1000 machines back in 2005 with this script. I've created a profile backup tool with it for use in migrating a 1200+ machine environment to XP from Win2K. It can do almost anything you can script.
The script is actually three things, two scripts (the one that will call psexec and the script that psexec will execute) and a text file with machine names, one per line like this:
PC1
PC2
PC3
and so on. This is versatile so that if you have over a couple of hundred machines and the script takes a while to run, break the machine list up into batches of 50 and run it on different machines at once (each machine will need psexec and the three files needed by the script). With that said, here we go.
The first script is the script that calls psexec. I usually put psexec in my C:\Windows\System32 directory since that is alrady part of the path but you can put it anywhere you want just make sure to use the whole path in the script.
I have the file pretty well remarked but I'll go over it anyway.
@echo off
echo off tells the script not to print the commands of the script to the screen, you'll see only output. The @ in front of echo off tells the shell not to print that command. It's for presentation only, during writing and troubleshooting of scripts I recommend you keep this line commented (two colons will do for that). Commented lines don't run at all.
The sample-list.txt file is the list of computer names that you want to run the script on.
This script will create a file called sample-dead.txt with a list of the machines that weren't on when you ran the script or that weren't able to be accessed. This will allow you run the script again but you can copy/paste or rename the names in this file to the -list file. The script cleans itself up, so it will delete the -dead list everytime.
cls - clear screen
It is now piping output to the screen as it checks the list and goes through the rest of the script.
The FOR .. DO loop is a powerful thing, what this one does is for each line in the -list file it goes to the :listcmd passing the %%i parameter (%%i is a temporary variable that the script is using to keep track of the machine name). The FOR command can have its own post, do a for /? and read up on it if you like.
As you've seen I have sections of the script labeled, :listcmd is such a label, this way I can tell the script to jump around the script (usually only downward so you've got to do some logic) depending on certain conditions. In the case of the FOR DO loop I have it verify that the line (ie the machine name) isn't blank "" and if it is not it will go on to the :listcmd otherwise it will jump down to the :errormsg and tell the operator something is up.
So now that the script has a machine name (the script is now displaying "Processing MachineName") it needs to confirm if that machine name is valid it does this by seeing if the ntsokrnl.exe file exists in the C:\Windir\System32 directory of the remote machine. How do I know this? Look at the path:
\\%1\admin$\system32\ntoskrnl.exe
\\%1 is the machine name
\admin$ is the admin share, this is a built in share on a windows box that points to the C:\Windir directory, note that this will find the directory if it's Winnt or Windows or ScoobyDoo unless something is seriously FUBAR'd on the machine or there are group policy settings in place to turn off admin shares. This also means you will need admin rights on the machines you wish to run this script on.
So if this file doesn't exist because the machine isn't on or we can't reach it, we put the machine name in the dead list by jumping down to :dead. If the machine is on we continue.
We are now in the working part of the script, the other stuff before this was for speed (trying to copy files to dead machines take a while to time out) and error checking.
First the script defines a set location for necessary files. This could be a special .exe you don't want left around on the worker PC when the script is done. You may not need this section, I use it to keep things like the resource kit tools on a central server but copy them over to the client for speedier running and then delete the .exe from the local client when done. Delprof is a good example but you could also get by by just calling the command from the psexec section too. This is a demo script only, I've not really fine tuned it since I wrote it in 2005.
So if you need to copy files over from the resource location you can do that.
PSEXEC time. I put psexec in my system32 so if you put it somewhere else you may need to include the path ie:
C:\temp\psexec.exe \\%1 -c -f _sample.cmd
The -c and -f switches tell psexec to copy the _sample.cmd over to the remote machine (c) even if it exists (f). This allows it to run remotely, this also means that in your scripts directory you should have:
_Sample.cmd
Sample.cmd
sample-list.txt
This is another reason I use that resource share, it allows me to only keep the scripts/sub-scripts (the _sample) and the list/dead files in my script directory.
What is _sample.cmd? Here it is for this demo:
Not much I know but structure really is more important than payload. Basically, I like to have a log of each time a script runs. You can do more fancy things, I've added the date and time of the script being run as a header as the log file grows but this is a demo.
scriptcmd.exe /arg1 /arg2 can be any exe you want. It can be a .exe you copied over from the resource share it can be a bunch of batch commands like:
Here's the thing, if the script hangs, this is where it is going to hang. This is the script you need to troubleshoot on a few test machines locally to verify it works OK before trying it out on 1or3 using psexec.
When the script completes psexec closes and you're back at the sample.cmd where you left off.
Now the script will delete any files you copied over from the resource share and the FOR DO loop continues, so it starts the loop again if there is another machine name or it ends if there is not. I never have the master script delete this list like I do the dead list. This and logs can be helpful in determining when and who ran scripts.
We've already gone over the errmsg and dead portions of the script.
Damn that took longer than I meant it to.
Do you ever walk alone like a drifter in the dark?
---------------------------------------------------------------
Steam: Bordone
Cygwin and BASH, baby! It only takes a few lines:
#!/bin/bash # rename.sh # usage: ./rename.sh (filename) # e.g. ./rename.sh myfile.txt # Cygwin mounts Windows filesystems in /cygdrive/(drive letter) REPLACE_DIR="/cygdrive/c/Documents and Settings/Edwin/My Documents/replace" FIND_PATH="/cygdrive/c/" echo "Edwin's Big Fat Search & Replace Script" echo "Searching for:" $1 find "$FIND_PATH" \( -path "$REPLACE_DIR" -prune \) -o -type f -name "$1" -print | while read FILE do echo "Found:" "$FILE" cp "$FILE" "$FILE".old echo "Renamed old version:" "$FILE".old cp "$REPLACE_DIR/$(basename "$FILE")" "$(dirname "$FILE")" echo "Copied" "$REPLACE_DIR/$(basename "$FILE")" "to location:" "$(dirname "$FILE")" doneThat will search FIND_PATH (excluding your directory of replacements, if it falls within FIND_PATH) for the filename you input, make a .old copy in the same directory, then grab the file of that name from your replacements folder and copy it over the original.
It wouldn't be hard to extend it so that you can input a search path at the command line instead of having to edit the hard-coded FIND_PATH in the script.
Needs to be tested, I just whipped it up and did a couple of trivial tests (does work just fine in Cygwin and with all those Windows path names with spaces in them, but as far as complete correctness goes, I'm not an expert in the finer points of GNU findutils - or even the duller ones, really). But if it's fully correct, it probably would have run correctly 10-15 years ago, and it will probably still be usable 10-15 years from now.
EDIT: changed quote tag to code tag, thanks nossid
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
There is a freeware app called flexible renamer that can do what you ask.
WOW: Lrlrbaselsta PS3 Tag: Baron-Of-Hell Steam:Baron_Of_Hell SSBB: 4425-3985-1734 TWITTER: Baron
Nerds. I smell nerds!
Fletcher wrote:
The forum has a code tag, no need to use quote and dots to indent.
Unofficial GWJ IRC Channel -- Stay awhile and listen
GWJ Starcraft 2 Ladder
EDIT: Lesson learned, use BBCode "code" instead of HTML "code" tag. Post removed.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
Edit: Problem resolved, post cleared to reduce clutter.
Unofficial GWJ IRC Channel -- Stay awhile and listen
GWJ Starcraft 2 Ladder
EDIT: I see what I did wrong. You used the BBCode "code" tag, and I used the HTML "code" tag listed under the post window in the "allowed HTML tags" list.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
Your post is using
<code>which breaks for me as well, while I used[code]in mine.Edit: too slow
Unofficial GWJ IRC Channel -- Stay awhile and listen
GWJ Starcraft 2 Ladder
Figuring that out was more work than writing the damn script in the first place.
If dealing with large files, it might be wise to change the line
cp "$FILE" "$FILE".oldto
mv "$FILE" "$FILE".old... so that it does actually rename instead of just make a copy with the .old name. I had it as a cp just so when I tested, I could just delete the .old files instead of having to rename my test file back.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
Miami, FLSeattle, WAI am going to try both methods tomorrow. While reading and experimenting with it today, do either of these scripts (PS or Cygwin) support archives? I noticed that the majority of files are in archives, which I didn't notice at the time?
Steam|Xbox Live|LoL|Twitter|
Cygwin is not like installing an application, it's effectively a mini unix box on your windows machine, and is useful for a huge range of things.
If the files you're looking for are in a zip file or something, you'll need to change the script to list the contents of the archives, piping to a grep command looking for your search name. If it finds it, you'd need to unzip the file into a temp location, rename the file, rezip it and delete the unzipped files. All pretty easy with a bash script.
XBL BGG GR
PSN: DudleySmith74
Windows command shell that's been around since NT is powerful enough to take on most search-and-rename operations. No warmed-over Unix utilities are needed. Familiarize yourself with options offered by the for command by typing help for at command line. Besides for, all you need are copy and rename commands, which are also built-in.
XBox Live | Twitter | Flickr
Right, my script is an uber for script (with lots of extra lines for documentation) but to make it shorter and sweeter it would just have to be (note I am still using a seperate txt file for machine names and assume admin rights):
I just like making the 'dead' list since running a script against 1000+ machines EVERY TIME gets a bit old, I'd like to make the batches smaller with each run until I've hit all the machines. I also include a lot of error checking and commentary since I assume I am not going to be working anywhere for the rest of my life and have been burned in the past by admins who don't like to document their work.
Do you ever walk alone like a drifter in the dark?
---------------------------------------------------------------
Steam: Bordone
That's the way to go!
XBox Live | Twitter | Flickr
Here's another barely-tested BASH script that:
* searches FIND_PATH for all ZIP files
* looks inside each ZIP file (unzip -l) to find a filename match with the command-line argument
* If it finds one, extracts the archive to a folder in TMP_PATH, moves the matched file to a .old filename, copies in the new one from REPLACE_PATH, and ZIPs it back up to the original archive location
#!/bin/bash FIND_PATH="/home/legion/Downloads" REPLACE_PATH="/home/legion/replace" TMP_PATH="/tmp" echo "Edwin's Big Fat Replace-it-in-a-ZIP Script" echo "Searching for:" $1 find "$FIND_PATH" -name "*.zip" -type f | while read FILE do RESULT=`unzip -l "$FILE" | awk '{print $4}' | grep -w "$1"` if [ ${#RESULT} -gt 0 ]; then echo "Found" "$1" "in" "$FILE" "(""$RESULT"")" mkdir "$TMP_PATH/$(basename "$FILE")" unzip -o "$FILE" -d "$TMP_PATH/$(basename "$FILE")" mv "$TMP_PATH/$(basename "$FILE")/$1" "$TMP_PATH/$(basename "$FILE")/$1.old" cp "$REPLACE_PATH"/"$1" "$TMP_PATH/$(basename "$FILE")/" zip -j "$FILE" "$TMP_PATH"/$(basename "$FILE")/* fi doneAnd a smaller one that does only the search, no file replacing (for quickly scanning those archives to find a file)
#!/bin/bash FIND_PATH="/home/legion/Downloads" echo "Edwin's Big Fat Find-it-in-a-ZIP Script" echo "Searching for:" $1 find "$FIND_PATH" -name "*.zip" -type f | while read FILE do RESULT=`unzip -l "$FILE" | awk '{print $4}' | grep -w "$1"` if [ ${#RESULT} -gt 0 ]; then echo "Found" "$1" "in" "$FILE" "(""$RESULT"")" fi doneAgain, barely tested, don't promise it won't
rm -rf /, but something you can poke at.I forgot to change my testing paths to the cygdrive paths, but I think you can handle that.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
The only thing I would recommend changing about *legion*'s scripts would be to use -iname instead of -name as an option to find. It won't really matter on windows, since the filesystem is case insensitive, but you could miss stuff on a *nix box.
PSN: Grakarg
XBLA: Grakarg
Steam: tyrian[GWJ]
Good call.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
I love the find command. find $path -$modfiers -exec {} \; = win on so many levels. I've done some really awful things with that command as a base (shell one liners rule!). I was even more pleased with it once I discovered it had the -ioptions.
PSN: Grakarg
XBLA: Grakarg
Steam: tyrian[GWJ]
As I have said many times, you can very nearly bring about world peace from the Unix command line.
Elewis17 wrote:
Only if your shell was compiled statically. As soon as you start relying on shared objects, all bets are off!
Hell, the unix command line has started plenty of wars in and of itself (shell purists, emacs vs vi[m], startx vs xinit)!
oh, uh.. finding stuff. replacing! On track, truly!
PSN: Grakarg
XBLA: Grakarg
Steam: tyrian[GWJ]
Yeah, but those are just wars. The emacs heathens need to be snuffed out.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
VIMer REPRESENT!
PSN: Grakarg
XBLA: Grakarg
Steam: tyrian[GWJ]
Your avatar dude looks like he's the one shouting that.
You should follow me on Twitter: @legion
Steam: *Legion* | Xbox Live: Legion SB | PSN: Legion_SB | Origin: LegionSB
Recently I wanted to write a script that was dead simple in Linux, but couldn't even begin to figure out how to do it in Windows. But I wanted to learn how. And I didn't feel like installing Cygwin. Then I found this:
An A-Z Index of the Windows XP command line
http://ss64.com/nt/
It's not quite the same set of commands, but it's a lot better than having nothing or knowing nothing.
Steam Id: Yoyoson | Amoebic: after climbing up BurningManCraft's leg, I was a little too close to the subject matter and I lost my sense of scale. I didn't realize the thing was bigger than his arm.
Yeah, Windows scripting isn't worthless, it's just not as strong as Bash shell scripting... plus, of course, the vast array of tiny Unix utilities that do just one thing extremely well.
The new Powershell looks pretty good. It's verbose, but looks quite powerful. It won't have anywhere near the same tool library, but it looks a HECK of a lot better than the old DOS-style scripting.
Elewis17 wrote:
Who says he isn't?
PSN: Grakarg
XBLA: Grakarg
Steam: tyrian[GWJ]