vbscript to select file and save directory? - batch-file

I'm currently working on a program that can start other Programs. Its in batch. But i need it to be user friendly. I want a code that can open a file select window, and save the directory of what i selected into a txt file (or csv or whatever). I'm new on VBS so forgive me if I'm missing something simple. But i searched for a while and got close, only to fail.
Here's what i have so far...
Set shell = CreateObject( "WScript.Shell" )
defaultLocalDir = shell.ExpandEnvironmentStrings("%USERPROFILE%") & "\Desktop"
Set shell = Nothing
file = ChooseFile(defaultLocalDir)
wscript.echo file
Function ChooseFile (ByVal initialDir)
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_OperatingSystem")
Dim winVersion
' This collection should contain just the one item
For Each objItem in colItems
'Caption e.g. Microsoft Windows 7 Professional
'Name e.g. Microsoft Windows 7 Professional |C:\windows|...
'OSType e.g. 18 / OSArchitecture e.g 64-bit
'Version e.g 6.1.7601 / BuildNumber e.g 7601
winVersion = CInt(Left(objItem.version, 1))
Next
Set objWMIService = Nothing
Set colItems = Nothing
If (winVersion <= 5) Then
' Then we are running XP and can use the original mechanism
Set cd = CreateObject("UserAccounts.CommonDialog")
cd.InitialDir = initialDir
cd.Filter = "ZIP files|*.zip|Text Documents|*.txt|Shell Scripts|*.*sh|All Files|*.*"
' filter index 4 would show all files by default
' filter index 1 would show zip files by default
cd.FilterIndex = 1
If cd.ShowOpen = True Then
ChooseFile = cd.FileName
Else
ChooseFile = ""
End If
Set cd = Nothing
Else
' We are running Windows 7 or later
Set shell = CreateObject( "WScript.Shell" )
Set ex = shell.Exec( "mshta.exe ""about: <input type=file id=X><script>X.click();new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(X.value);close();resizeTo(0,0);</script>""" )
ChooseFile = Replace( ex.StdOut.ReadAll, vbCRLF, "" )
Set ex = Nothing
Set shell = Nothing
End If
End Function

Maybe you are looking for something like next bat code snippet?
#echo OFF
SETLOCAL enableextensions
set "_chosen="
if exist "_saved.txt" (
rem read previously saved value
<"_saved.txt" set /P "_chosen="
echo read previously saved value
) else (
for /f "delims=" %%G in ('
cscript //NOLOGO "D:\VB_scripts\SO\31656148.vbs"
') do (
set "_chosen=%%G"
rem save value to a file
>"_saved.txt" echo(%%G
echo file chosen, value saved
)
)
if defined _chosen (
echo("%_chosen%"
) else (
echo no file chosen
)

Related

close console after VBS script executes

I have a multi screen computers system. Once in a while, for a reason I don't understand, the dialog boxes are on the wrong monitor. For instance, I'll have a program running in monitor A and an OK box will open in monitor D. This is very frustrating.
I found a VBS script called "PositionDialogs.vbs" found here: https://www.realtimesoft.com/ultramon/scripts/
Const SNAP_TO_MONITOR = False 'set this to True to ensure dialogs aren't placed between two monitors
Const INTERVAL = 2 'number of seconds the script waits before enumerating open windows again
Set sys = CreateObject("UltraMon.System")
Set wnd = CreateObject("UltraMon.Window")
Set wndParent = CreateObject("UltraMon.Window")
'create the two maps used to store positioned windows
Set arrAdd = CreateObject("Scripting.Dictionary")
Set arrLookup = CreateObject("Scripting.Dictionary")
Do While True
'enumerate all application windows
For Each w In wnd.GetAppWindows(True)
If w.HWndParent <> 0 Then
wndParent.HWnd = w.HWndParent
move = True
If arrLookup.Exists(w.HWnd) = True Then move = False
arrAdd.Add w.HWnd, 0
If move = True Then
If SNAP_TO_MONITOR = False Then
If w.Monitor <> wndParent.Monitor Then
w.Monitor = wndParent.Monitor
w.ApplyChanges 1 + 2 'WNDCHANGE_RESIZE_TO_FIT + WNDCHANGE_CLIP_TO_WORKSPACE
End If
Else
Set parentMon = sys.Monitors(wndParent.Monitor - 1)
parentLeft = parentMon.WorkLeft
parentTop = parentMon.WorkTop
parentRight = parentLeft + parentMon.WorkWidth
parentBottom = parentTop + parentMon.WorkHeight
dlgLeft = w.Left
dlgTop = w.Top
dlgRight = dlgLeft + w.Width
dlgBottom = dlgTop + w.Height
If dlgLeft < parentLeft Then
w.Left = parentLeft
ElseIf dlgRight > parentRight Then
w.Left = parentRight - w.Width
End If
If dlgTop < parentTop Then
w.Top = parentTop
ElseIf dlgBottom > parentBottom Then
w.Top = parentBottom - w.Height
End If
w.ApplyChanges 0
End If
End If
End If
Next
'swap maps, then clear arrAdd. this way we don't have entries for windows which no longer exist
Set temp = arrLookup
Set arrLookup = arrAdd
Set arrAdd = temp
Set temp = Nothing
arrAdd.RemoveAll
WScript.Sleep INTERVAL * 1000
Loop
that will move the dialog box to whatever monitor called it.
I have it running on Windows startup using a batch file, and it runs as a process. My problem is that the console window that shows doesn't go away unless I click the X to close it.
The bath files looks like this:
wscript PositionDialogs.vbs
exit
I assume there is something I can add to the script to make it close after it loads itself into memory? If so, what?
aschipf was correct.
I made the batch file
start PositionDialogs.vbs
exit
(used START instead of WSCRIPT) and it closed as expected, while the process still ran in task manager

How can I overwrite a file's "Date created" with its "Date modified" in Windows?

I have a folder full of files with correct Date modified times but incorrect date created times. I want to set the dateCreated file attribute to the time of the Date modified.
The closest thing I have to my solution is a batch file that sets the date modified time to the when the file was created ( https://stackoverflow.com/a/24951475/2780666 ) but I want to do the opposite. How do I do this if possible?
Assuming the script you found works, I would try the following.
#if (#This==#IsBatch) #then
#echo off
rem **** batch zone *********************************************************
setlocal enableextensions disabledelayedexpansion
set "targetFolder=%~1"
if not defined targetFolder set "targetFolder=%cd%"
rem call javascript part of batch file
cscript //nologo //e:Javascript "%~f0" /startFolder:"%targetFolder%"
rem End of batch area. End batch execution before reaching js zone
endlocal
exit /b
#end
// **** Javascript zone *****************************************************
if (!WScript.Arguments.Named.Exists('startFolder')) {
// if no start folder is given, leave
WScript.Quit(1);
};
// retrieve start folder
var startFolder = WScript.Arguments.Named.Item('startFolder');
// instantiate needed components
var fso = WScript.CreateObject('Scripting.FileSystemObject');
var shell = WScript.CreateObject('Shell.Application');
// recursive function to set the ModifyDate to the CreationDate
(function processFolder( folderPath ){
// test for valid paths
folderPath = fso.GetAbsolutePathName((folderPath || '' ));
if (!fso.FolderExists(folderPath)) return ;
// retrieve a reference to the folder namespace
var folderNS = shell.NameSpace(folderPath);
// process files inside this folder
for (var files = new Enumerator(fso.GetFolder( folderPath ).Files ); !files.atEnd() ; files.moveNext()){
var file = files.item();
WScript.StdOut.WriteLine( file.Path );
folderNS.ParseName( file.Name ).DateCreated = file.ModifyDate;
};
// process files under child folders
for (var folders = new Enumerator(fso.GetFolder( folderPath ).SubFolders); !folders.atEnd() ; folders.moveNext()){
processFolder( folders.item().Path );
};
})( startFolder );
WScript.Quit(0);

Split text file into smaller multiple text file using command line

I have multiple text file with about 100,000 lines and I want to split them into smaller text files of 5000 lines each.
I used:
split -l 5000 filename.txt
That creates files:
xaa
xab
aac
xad
xbe
aaf
files with no extensions. I just want to call them something like:
file01.txt
file02.txt
file03.txt
file04.txt
or if that is not possible, i just want them to have the ".txt" extension.
I know the question was asked a long time ago, but I am surprised that nobody has given the most straightforward Unix answer:
split -l 5000 -d --additional-suffix=.txt $FileName file
-l 5000: split file into files of 5,000 lines each.
-d: numerical suffix. This will make the suffix go from 00 to 99 by default instead of aa to zz.
--additional-suffix: lets you specify the suffix, here the extension
$FileName: name of the file to be split.
file: prefix to add to the resulting files.
As always, check out man split for more details.
For Mac, the default version of split is dumbed down. You can install the GNU version using the following command. (see this question for more GNU utils)
brew install coreutils
and then you can execute the above command by replacing split with gsplit. Check out man gsplit for details.
Here's an example in C# (cause that's what I was searching for). I needed to split a 23 GB csv-file with around 175 million lines to be able to look at the files. I split it into files of one million rows each. This code did it in about 5 minutes on my machine:
var list = new List<string>();
var fileSuffix = 0;
using (var file = File.OpenRead(#"D:\Temp\file.csv"))
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
list.Add(reader.ReadLine());
if (list.Count >= 1000000)
{
File.WriteAllLines(#"D:\Temp\split" + (++fileSuffix) + ".csv", list);
list = new List<string>();
}
}
}
File.WriteAllLines(#"D:\Temp\split" + (++fileSuffix) + ".csv", list);
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET /a fcount=100
SET /a llimit=5000
SET /a lcount=%llimit%
FOR /f "usebackqdelims=" %%a IN ("%sourcedir%\q25249516.txt") DO (
CALL :select
FOR /f "tokens=1*delims==" %%b IN ('set dfile') DO IF /i "%%b"=="dfile" >>"%%c" ECHO(%%a
)
GOTO :EOF
:select
SET /a lcount+=1
IF %lcount% lss %llimit% GOTO :EOF
SET /a lcount=0
SET /a fcount+=1
SET "dfile=%sourcedir%\file%fcount:~-2%.txt"
GOTO :EOF
Here's a native windows batch that should accomplish the task.
Now I'll not say that it'll be fast (less than 2 minutes for each 5Kline output file) or that it will be immune to batch character-sensitivites. Really depends on the characteristics of your target data.
I used a file named q25249516.txt containing 100Klines of data for my testing.
Revised quicker version
REM
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET /a fcount=199
SET /a llimit=5000
SET /a lcount=%llimit%
FOR /f "usebackqdelims=" %%a IN ("%sourcedir%\q25249516.txt") DO (
CALL :select
>>"%sourcedir%\file$$.txt" ECHO(%%a
)
SET /a lcount=%llimit%
:select
SET /a lcount+=1
IF %lcount% lss %llimit% GOTO :EOF
SET /a lcount=0
SET /a fcount+=1
MOVE /y "%sourcedir%\file$$.txt" "%sourcedir%\file%fcount:~-2%.txt" >NUL 2>nul
GOTO :EOF
Note that I used llimit of 50000 for testing. Will overwrite the early file numbers if llimit*100 is gearter than the number of lines in the file (cure by setting fcount to 1999 and use ~3 in place of ~2 in file-renaming line.)
You can maybe do something like this with awk
awk '{outfile=sprintf("file%02d.txt",NR/5000+1);print > outfile}' yourfile
Basically, it calculates the name of the output file by taking the record number (NR) and dividing it by 5000, adding 1, taking the integer of that and zero-padding to 2 places.
By default, awk prints the entire input record when you don't specify anything else. So, print > outfile writes the entire input record to the output file.
As you are running on Windows, you can't use single quotes because it doesn't like that. I think you have to put the script in a file and then tell awkto use the file, something like this:
awk -f script.awk yourfile
and script.awk will contain the script like this:
{outfile=sprintf("file%02d.txt",NR/5000+1);print > outfile}
Or, it may work if you do this:
awk "{outfile=sprintf(\"file%02d.txt\",NR/5000+1);print > outfile}" yourfile
Syntax looks like:
$ split [OPTION] [INPUT [PREFIX]]
where prefix is
PREFIXaa, PREFIXab, ...
Just use proper one and youre done or just use mv for renameing.
I think
$ mv * *.txt
should work but test it first on smaller scale.
:)
This "File Splitter" Windows command line program works nicely: https://github.com/dubasdey/File-Splitter
It's open source, simple, documented, proven, and worked for me.
Example:
fsplit -split 50 mb mylargefile.txt
My requirement was a bit different. I often work with Comma Delimited and Tab Delimited ASCII files where a single line is a single record of data. And they're really big, so I need to split them into manageable parts (whilst preserving the header row).
So, I reverted back to my classic VBScript method and bashed together a small .vbs script that can be run on any Windows computer (it gets automatically executed by the WScript.exe script host engine on Window).
The benefit of this method is that it uses Text Streams, so the underlying data isn't loaded into memory (or, at least, not all at once). The result is that it's exceptionally fast and it doesn't really need much memory to run. The test file I just split using this script on my i7 was about 1 GB in file size, had about 12 million lines of test and made 25 part files (each with about 500k lines each) – the processing took about 2 minutes and it didn’t go over 3 MB memory used at any point.
The caveat here is that it relies on the text file having "lines" (meaning each record is delimited with a CRLF) as the Text Stream object uses the "ReadLine" function to process a single line at a time. But hey, if you're working with TSV or CSV files, it's perfect.
Option Explicit
Private Const INPUT_TEXT_FILE = "c:\bigtextfile.txt" 'The full path to the big file
Private Const REPEAT_HEADER_ROW = True 'Set to True to duplicate the header row in each part file
Private Const LINES_PER_PART = 500000 'The number of lines per part file
Dim oFileSystem, oInputFile, oOutputFile, iOutputFile, iLineCounter, sHeaderLine, sLine, sFileExt, sStart
sStart = Now()
sFileExt = Right(INPUT_TEXT_FILE,Len(INPUT_TEXT_FILE)-InstrRev(INPUT_TEXT_FILE,".")+1)
iLineCounter = 0
iOutputFile = 1
Set oFileSystem = CreateObject("Scripting.FileSystemObject")
Set oInputFile = oFileSystem.OpenTextFile(INPUT_TEXT_FILE, 1, False)
Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
If REPEAT_HEADER_ROW Then
iLineCounter = 1
sHeaderLine = oInputFile.ReadLine()
Call oOutputFile.WriteLine(sHeaderLine)
End If
Do While Not oInputFile.AtEndOfStream
sLine = oInputFile.ReadLine()
Call oOutputFile.WriteLine(sLine)
iLineCounter = iLineCounter + 1
If iLineCounter Mod LINES_PER_PART = 0 Then
iOutputFile = iOutputFile + 1
Call oOutputFile.Close()
Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
If REPEAT_HEADER_ROW Then
Call oOutputFile.WriteLine(sHeaderLine)
End If
End If
Loop
Call oInputFile.Close()
Call oOutputFile.Close()
Set oFileSystem = Nothing
Call MsgBox("Done" & vbCrLf & "Lines Processed:" & iLineCounter & vbCrLf & "Part Files: " & iOutputFile & vbCrLf & "Start Time: " & sStart & vbCrLf & "Finish Time: " & Now())
here is one in c# that doesn't run out of memory when splitting into large chunks! I needed to split 95M file into 10M x line files.
var fileSuffix = 0;
int lines = 0;
Stream fstream = File.OpenWrite($"{filename}.{(++fileSuffix)}");
StreamWriter sw = new StreamWriter(fstream);
using (var file = File.OpenRead(filename))
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
sw.WriteLine(reader.ReadLine());
lines++;
if (lines >= 10000000)
{
sw.Close();
fstream.Close();
lines = 0;
fstream = File.OpenWrite($"{filename}.{(++fileSuffix)}");
sw = new StreamWriter(fstream);
}
}
}
sw.Close();
fstream.Close();
I have created a simple program for this and your question helped me complete the solution...
I added one more feature and few configurations.
In case you want to add a specific character/ string after every few lines (configurable). Please go through the notes.
I have added the code files :
https://github.com/mohitsharma779/FileSplit

edit a specific line in text file using batch command

I have a textfile which contains the following text.
"Module"
{
"ModuleSignature" = "8:MergeModule.6F1248514B3047E99E4EE8A129CB8605"
"Version" = "8:1.0.0.0"
"Title" = "8:uoipmsm"
"Subject" = "8:"
"Author" = "8:Microsoft"
"Keywords" = "8:"
"Comments" = "8:"
"SearchPath" = "8:"
"UseSystemSearchPath" = "11:TRUE"
"TargetPlatform" = "3:1"
"PreBuildEvent" = "8:"
"PostBuildEvent" = "8:"
"RunPostBuildEvent" = "3:0"
}
In the above; I want to change the Version number which I will give when I trigger a build from a tool.
I wanna pass a parameter $Version in batch file, it has to take the version number from the tool I use and update the same in that text file.
For ex: in the above text i wanna code it as "Version" = "8:$Version" hence when ever I provide a version number while triggering a build, it has to update the same in this text file.
Could you please guide me how to edit the specific line. I am new to windows batch scripting.
and i hav to add one more point... in the text file i have to modify the version in the line number 399. So the batch file has to jump to line num 399 in that text file and modify the same. Kindly help me to fix the same ...
I had saved the above script in a text pad and saved the same as ver.bat; and also in the same folder I saved the Intext file. When I mention the line number which to be replaced, it is removing the contents which are present after "=" symbol, from line 1 to 399 .
Before running the batch file:
"ModuleSignature" = "8:MergeModule.6F1248514B3047E99E4EE8A129CB8605"
"Version" = "8:1.0.0.0"
"Title" = "8:uoipmsm"
"Subject" = "8:"
"Author" = "8:Microsoft"
"Keywords" = "8:"
"Comments" = "8:"
"SearchPath" = "8:"
"UseSystemSearchPath" = "11:TRUE"
"TargetPlatform" = "3:1"
"PreBuildEvent" = "8:"
"PostBuildEvent" = "8:"
"RunPostBuildEvent" = "3:0"
I mentioned the line number as 10 and after tat if I run the batch file; i get the following output;
"ModuleSignature" =
"Version" = ""
"Title" =
"Subject" =
"Author" =
"Keywords" =
"Comments" =
"SearchPath" =
"UseSystemSearchPath" =
"TargetPlatform" =
"PreBuildEvent" =
"PostBuildEvent" =
"RunPostBuildEvent" =
Any idea??
This should do it. Of course this will have to change if your input file ever changes. You may also have to make an adjustment if there are blank lines in your input file (they don't count). Note that you only need to pass the version (not the entire new line 399). In your example that would be "8:1.0.0.0". Best to quote the string in case there are ever spaces in there.
By way of explanation, this will:
This bat file must be called with an argument
Use a FOR LOOP to echo the first 398 lines to a temp file
Add a new line 399 using the version passed as an argument
Use MORE to append the remaining lines to the temp file
Copy the temp file to the original file
Delete the temp file
x
#echo off
REM %1=Version (use quotes if there are spaces in version)
set ReplaceLine=399
set InFile=Test.txt
set TempFile=TempTest.txt
if exist "%TempFile%" del "%TempFile%"
if "%~1"=="" (
color CF
echo.This program must be called with an argument!
pause
goto :eof
)
setlocal enabledelayedexpansion
set /A Cnt=1
for /F "tokens" %%a in (%InFile%) do (
echo.%%a>> "%TempFile%"
set /A Cnt+=1
if !Cnt! GEQ %ReplaceLine% GOTO :ExitLoop
)
:ExitLoop
endlocal
echo."Version" = "%~1">> "%TempFile%"
more +%ReplaceLine% < "%InFile%">> "%TempFile%"
copy /y "%TempFile%" "%InFile%"
del "%TempFile%"
goto :eof

running a batch file from another batch file as an admisitrator

I need to run a batch file (setup.bat) which will call another batch file (make_dir.bat) which will create a folder in the "C:\Program Files" directory. This is for an internal installer. All the users will be logged in with their user names but will have local administrator rights. I've tried two approaches but neither work.
Approach 1:
SET PRGFILES=%programfiles%\mySoftware
SET admin=N
SET domain=%USERDOMAIN%\
IF /i "%domain%" EQU "%computername%\" set domain=
SET user=%domain%%username%
FOR /f "Tokens=*" %%a IN ('net localgroup administrators^|find /i "%user%"') DO SET admin=Y
IF "%admin%"=="Y" (
MD "%PRGFILES%"
)
This says Access is denied
Approach 2:
runas /user:%Username% shell\make_dir.bat
where make_dir.bat is
md "%programfiles%\mySoftware"
This asks for the current username and password but somehow fails after that. I have checked that all users have local admin rights and can manually create a folder in their programfiles folder.
Thanks for the help.
I use such script to run .bat file as administrator, using JScript:
var batch = "fixuac.bat"
var fso = new ActiveXObject("Scripting.FileSystemObject");
var curdir = fso.GetParentFolderName(WScript.ScriptFullName);
var wbemFlagReturnImmediately = 0x10;
var wbemFlagForwardOnly = 0x20;
var objWMIService = GetObject("winmgmts:\\\\.\\root\\CIMV2");
// var objWMIService = GetObject("winmgmts:" + "{impersonationLevel=impersonate}!\\" + "." + "\root\cimv2");
var colItems = objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", "WQL",
wbemFlagReturnImmediately | wbemFlagForwardOnly);
var enumItems = new Enumerator(colItems);
var objItem = enumItems.item();
// http://en.wikipedia.org/wiki/Ver_(command)
var major_ver = objItem.Version.split(".")[0];
var objShell = new ActiveXObject("shell.application");
// http://msdn.microsoft.com/en-us/library/windows/desktop/gg537745.aspx
// Shell.ShellExecute method
// iRetVal = Shell.ShellExecute( sFile, [ vArguments ], [ vDirectory ], [ vOperation ], [ vShow ] )
// If (vShow==1) open the application with a normal window.
// Check for Vista and upper.
if (major_ver >= 6) {
// Request admin permission.
objShell.ShellExecute(batch, curdir, "", "runas", 1);
} else {
objShell.ShellExecute(batch, curdir);
}

Resources