Windows batch: Remove lines from a merged file - windows

I am currently making a batch file that merges both, the output from systeminfo, and ipconfig:
#ECHO OFF
pause
systeminfo > "%computername% SystemInfo.txt"
ipconfig >> "%computername% SystemInfo.txt"
"%computername% systeminfo.txt"
The code runs fine and nicely, also independently from OS version and OS language as far as I can tell. My problem though, lies with the systeminfo dump. It lists all 100+ hotfixes that have ever been installed in the machine that is runs on, making the txt file barely legible:
<useful info>
[01]: File 1
[02]: File 1
[03]: File 1
[04]: File 1
....
[150]: file 1
etc...
<useful info>
There's also another problem, namely that this batch file has to run on computers that either run Dutch windows or English windows, meaning that I can't filter on words, because those hotfixes and the words will be different on every computer. Anybody have a nice sollution to this problem.
Note: I have seen it solved the other way around, leaving only the relevant info using findstr. But, because that depends on the language, it is not a viable option.
Edit: The hotfixes are named differently on different OS'es as well, meaning that I can't filter on those. Example: on the XP SP3 I tested, most of the list will be compromised of hotfixes called "[##]file1" on vista however, you will see hex values in the list.

EDIT
My original answer did not work, but I have another idea that works as long as the number and order of each systeminfo header is consistent. I am relying on the fact that the Hotfix(s): is always the 31st header.
#echo off
setlocal enableDelayedExpansion
>systemInfo.txt (
set cnt=0
for /f "delims=" %%A in ('systeminfo') do (
set "ln=%%A"
if "!ln:~0,1!"==" " (if !cnt! neq 31 echo !ln!) else (
echo !ln!
set /a cnt+=1
)
)
ipconfig
)
If the number and/or order of the headers can change, then I don't see how there can be a solution, other then to bite the bullet and look for the specific header text, accounting for all languages that you need to support.
Original failed answer
I don't know how reliable this is. It works for me on my machine, but it would not surprise me if on some machines it strips things it shouldn't.
>systemInfo.txt (
systeminfo|findstr /vxrc:" \[[0-9]*\]: [^ ]*"
ipconfig
)
All my hotfixes begin with KB, followed by a string of numbers. If this is always true, then the above could be improved as:
>systemInfo.txt (
systeminfo|findstr /vxrc:" \[[0-9]*\]: KB[0-9]*"
ipconfig
)

I don't want to figure it out, but you could format it based on the csv output from systeminfo.
systeminfo /fo csv > info.csv
The output, for any language will basically be:
(headers)"<col>", "<col>", "<col>" <...> "<col>"<newline>
(data)"<col>", "<col>", "<col>" <...> "<col>"
The hotfix column is the second to last column. so you could split it by quotes, and ignore that field. It'll have a bunch of crap in it, but it will still be "hotfix, hotfix, hotfix," so you can just remove the whole thing in quotes.
String manipulation in batch is awful if you ask me. If this were me, I'd do it in a language with a string library and call that instead.

Related

Script to move all files starting with the same 7 letters in a different folder named after first 7 chars of its future content

All files are in a directory (over 500 000 files), named in the following pattern
AR00001_1
AR00001_2
AR00001_3
AR00002_1
AR00002_2
AR00002_3
I need a script, can be both batch or unix shell that takes everything with AR00001 and moves it into a new folder that will be called AR00001, and does the same for AR00002 files etc
Here's what I've been trying to figure out until now
for f in *_*; do
DIR="$( echo ${f%.*} | tr '_' '/')"
mkdir -p "./$DIR"
mv "$f" "$DIR"
done
Thanks
// Update
Ran this in the CMD
for %F in (c:\test\*) do (md "d:\destination\%~nF"&move "%F" "d:\destination\%~nF\") >nul
Seems to be almost what I wanted, except that it does not take the first 7 characters as a substring but instead creates a folder for each file :/ I'm trying to mix it with your solutions
#echo off
setlocal enabledelayedexpansion
for %%a in (???????_*) do (
set "x=%%a"
set "x=!x:~0,7!"
md "!x!" >nul
move "!x!*" "!x!\" 2>nul
)
for every matching file do:
- get the first 7 characters
- create a folder with that name (ignore error message, if exist)
- move all files that start with those 7 characters (ignore errormessages, if files doesn't exist (already moved))
The following achieves the desired effect and checks for non-existence of the target directory each time before creating it.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set "TOBASE=c:\target\"
set "MATCHFILESPEC=AR*"
for %%F in ("%MATCHFILESPEC%") do (
set "FILENAME=%%~nF"
set "TOFOLDER=%TOBASE%!FILENAME:~0,7!"
if not exist "!TOFOLDER!\" md "!TOFOLDER!"
move "%%F" "!TOFOLDER!" >nul
)
endlocal
In the move command, by moving only the current file rather than including a wildcard, we ensure that we're not eating up file names that might be about to appear the next time around the loop. Keeping it simple, assuming that efficiency is not of prime importance.
I'd recommend prototyping by creating batch files (with a .bat or .cmd extension) rather than trying to do complex tasks interactively using on one-liners. The behaviour can be different and there are more things you can do in a batch file, such as using setlocal to turn on delayed expansion of variables. It's also just a pain writing for loops using the %F interactively, only to have to remember to convert all those to %%F, %%~nF, etc. when pasting into a batch file for posterity.
One word of caution: with 500,000 files in the folder, and all of the files having very similar prefixes, if your file system has 8.3 directory naming turned on (which is often the default) it is possible to run into problems using wildcards. This happens as the 8.3 namespace gets more and more busy and there are fewer and fewer options for ways the file name can be encoded in 8 characters. (The hash table fills up and starts overflowing into unexpected file names).
One solution is to turn that feature off on the server but that may have severe implications for any legacy applications. To see what the file looks like in 8.3 naming scheme, you can do, e.g.:
dir /x /p AR*
... which might give you something like (where the left hand name is the one converted to 8.3):
ARB900~1.TST AR15467_RW322.tst
AR85E3~1.TST AR15468_RW322.tst
ARDDFE~1.TST AR15469_RW322.tst
AR1547~1.TST AR15470_RW322.tst
AR1547~2.TST AR15471_RW322.tst
...
In this example, since the first two characters seem to be maintained, there should be no conflict.
So for example if I say for %a in (AR8*) do #echo %a I get what might at first seem to be incorrect:
AR15468_RW322.tst
AR18565_RW322.tst
AR20376_RW322.tst
AR14569_RW322.tst
AR17278_RW322.tst
...
But this is actually correct; it is all the files that match AR8* in both the long file name and short file name formats.
Edit: I am aware in retrospect that this solution looks very similar to Stephan's, and I had browsed through the existing answers before starting work on my own, so I should credit him. I will try and save face by pointing out a benefit of Stephan's solution. Its use of wildcards should circumvent any 8.3 naming issue: by specifying the wildcard as ???????_*, it only catches the long file names and won't match any of the converted 8.3 file names (all of which are devoid of underscores in that position). Similarly, a wildcard such as AR?????_* would do the same.
With bash, you'd write:
for f in *; do
[[ -d $f ]] && continue # skip existing directories
prefix=${f:0:7} # substring of first 7 characters
mkdir -p "$prefix" # create the directory if it does not exist
mv "$f" "$prefix" # and move the file
done
For the substring expansion, see https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion -- this is probably the bit you're missing.

set /p or alternative on MS-DOS (Windows ME) [duplicate]

I've written a program that returns keycodes as integers for DOS
but i don't know how to get it's output as a variable.
Note: I'm using MS-DOS 7 / Windows 98, so i can't use FOR /F or SET /P
Does anyone know how i could do that?
A few solutions are described by Eric Pement here. However, for older versions of cmd the author was forced to use external tools.
For example, program tools like STRINGS by Douglas Boling, allows for following code:
echo Greetings! | STRINGS hi=ASK # puts "Greetings!" into %hi%
Same goes for ASET by Richard Breuer:
echo Greetings! | ASET hi=line # puts "Greetings!" into %hi%
One of alternative pure DOS solutions needs the program output to be redirected to the file (named ANSWER.DAT in example below) and then uses a specially prepared batch file. To cite the aforementioned page:
[I]n the batch file we need to be able to issue the command
set MYVAR={the contents of ANSWER.DAT go here}. This is a difficult task, since MS-DOS doesn't offer an easy way to prepend "set MYVAR=" to a file [...]
Normal DOS text files and batch files end all lines with two consecutive bytes: a carriage return (Ctrl-M, hex 0D, or ASCII 13) and a linefeed (Ctrl-J, hex 0A or ASCII 10). In the batch file, you must be able to embed a Ctrl-J in the middle of a line.
Many text editors have a way to do this: via a Ctrl-P followed by Ctrl-J (DOS EDIT with Win95/98, VDE), via a Ctrl-Q prefix (Emacs, PFE), via direct entry with ALT and the numeric keypad (QEdit, Multi-Edit), or via a designated function key (Boxer). Other editors absolutely will not support this (Notepad, Editpad, EDIT from MS-DOS 6.22 or earlier; VIM can insert a linefeed only in binary mode, but not in its normal text mode).
If you can do it, your batch file might look like this:
#echo off
:: assume that the datafile exists already in ANSWER.DAT
echo set myvar=^J | find "set" >PREFIX.DAT
copy PREFIX.DAT+ANSWER.DAT VARIAB.BAT
call VARIAB.BAT
echo Success! The value of myvar is: [%myvar%].
:: erase temp files ...
for %%f in (PREFIX.DAT ANSWER.DAT VARIAB.BAT) do del %%f >NUL
Where you see the ^J on line 3 above, the linefeed should be embedded at that point. Your editor may display it as a square box with an embedded circle.

How to output multiple user input values into a single variable in BATCH using Windows 7?

SOLVED!
Update: It figures moments after posting for help which is something I never do I'd figure it out...I tend to over think things, and that was the case here, it was just so simple! >.<
Solution:
(This worked under Windows 7 Ultimate 64 Bit)
Set var=
Set var=SomeText %var1% %var2% %var3%
Echo %var% > output.txt
See an explanation in my answer below.
I've been searching and trying several posts here similar to my question for hours with no success. I'm not new to Programming in BATCH but I have memory problems and thus can't always remember things. It also doesn't help that I program in other languages on other platforms which usually means I'm trying to use *nix shell commands in my Windows Batch scripts >.<
I've gotten quite close with some examples but nothing that works as needed.
Ideally, I'd like this work to work on Windows 7, 8, 8.1, Vista and 10 as that is the intended target.
This is what I need to accomplish:
The user will answer a series of questions, each question is stored into a .txt file (or variable if you prefer. I just used text files because of a past project where I ran into issues with variables that couldn't be solved and text files worked). The lines in each text file will need to be output into a single text file, on a single line which will then be read back in as a variable and run. Again, you could just use and combine the variables in your example if that's easier for you or both of us ;P
This is a snippet example of how I was doing it
SET file1=
SET /P file1=file1:%=%
ECHO %file1% > file1.txt
Then
copy /b file1.txt + file2.txt + file3.txt + file4.txt output.txt
Here is how I'd like the result to look
toolkit /S "C:\ToolKit Bravo\Data\etc" "D:\ToolKit Bravo\Data\Ops"
The "" quotation marks are necessary. The output MUST be EXACTLY as shown above for the example I've given. The "/S" & paths are variable NOT fixed!
Here is the best I've been able to come up with using variables..
"toolkit /S "C:\ToolKit Bravo\Data\etc" "D:\ToolKit Bravo\Data\Ops""
Update 2 - An explanation as requested:
The paths in the above example directly above this are not fixed! This was an Example Only. "toolkit" is fixed, this doesn't change. "/S" is an option selected by the user to pass on to the "toolkit". Both the source and destination paths are again input by the user in "quotation" marks. They're not fixed paths.
As you can see the result is surrounded by quotations which is NOT acceptable. And Please remember, I NEED the quotations around the paths in the end result, so removing them all is NOT an option!
Any help is greatly appreciated! Thank you for your time.
Just take all of the characters between the quotes.
SET X="toolkit /S "C:\ToolKit Bravo\Data\etc" "D:\ToolKit Bravo\Data\Ops""
ECHO %X%
SET Y=%x:~1,-1%
ECHO %Y%
Solution:
This solved my problem under Windows 7 Ultimate 64 Bit
Set var=
Set var=SomeText %var1% %var2% %var3%
Echo %var% > textfile.txt
Using the SET command I first made sure the variable or var for short was empty. Using this command:
Set var=
I then proceeded to create my variable using all of the other variables I had created and wanted to combine using this line of code:
Set var=SomeText %var1% %var2% %var3%
Note that I have preceded the variables with "SomeText". This is where I'll place the name of the .exe I'm passing the arguments to, but it can be anything you want included. I also need spaces between each variable so I've left spaces between them in the example code. If you don't want the spaces simply remove them, and you'll have 1234, instead of 1 2 3 4.
Finally I send the combined variable out to a .txt file.
Echo %var% > textfile.txt
However, you could also simply call the new variable now like this:
%var%

On windows, how would I detect the line ending of a file?

I've seen answers to the questions, but those answers are not from a windows perspective from what I can tell.
Windows uses CR LF, Unix uses LF, Mac uses LF and classic mac uses something else. I don't have the brainpower to tell that somehow, if a file is using a different line ending than what I am typing, I get errors when trying to run the script/program which frankly, don't make much sense. After conversion, the script works just fine.
Is there anyway to preemptively check what line endings a file uses, on Windows?
use a text editor like notepad++ that can help you with understanding the line ends.
It will show you the line end formats used as either Unix(LF) or Macintosh(CR) or Windows(CR LF) on the task bar of the tool.
you can also go to View->Show Symbol->Show End Of Line to display the line ends as LF/ CR LF/CR.
Steps:
From the following link download binaries and dependencies zip files:
http://gnuwin32.sourceforge.net/packages/file.htm
Extract their content under the same directory (merge existing directories).
e.g. under c:\gnuwin32
Then you can execute:
c:\gnuwin32\bin\file.exe my-lf-file.txt
my-lf-file.txt; ASCII text
c:\gnuwin32\bin\file.exe my-crlf-file.txt
my-crlf-file.txt; ASCII text, with CRLF line terminators
Of course you can add c:\gnuwin32\bin to your %PATH% variable, to be able to access it without providing the full path.
UPDATE:
If you have git installed you can launch git-bash and run file command from there.
Or you can install this subsystem, as described in the official Microsoft documentation, and get access to the file command.
I too am looking for a "native" windows scripting solution. So far, just have to read a line or 2 in VB in binary fashion and inspect the characters.
One tool to check "manually" is Notepad++. The status bar has a newline style indicator on the right end next to the file encoding indicator.
It looks like this in version 7.5.6
Other editors with Hex mode can show you also.
In Powershell, this command returns "True" for a Windows style file and "False" for a *nix style file.
(Get-Content '\\FILESERVER0001\Fshares\NETwork Shares\20181206179900.TXT' -Raw) -match "\r\n$"
This came from Matt over here: https://stackoverflow.com/a/35354009/1337544
In a batch file, you can try converting the file to CRLF and checking if its size increases:
rem check-crlf.bat
#echo off
setlocal
call type "%~1" | c:\Windows\System32\find.exe "" /v > "%~1.temp"
set size1=%~z1
rem add 2 in case the file doesn't have a trailing newline, since find will add it
set /a size1plus2=%size1%+2
call :setsize2 "%~1.temp%"
for /f %%a in ('c:\Windows\System32\findstr /R /N "^" "%~1" ^| c:\Windows\System32\find /C ":"') do set lines=%%a
if %size1plus2% equ %size2% (
if %lines% equ 2 (
echo File uses LF line endings!
) else (
echo File uses CRLF or has no line endings!
)
) else (
if %size1% lss %size2% (
echo File uses LF line endings!
) else (
echo File uses CR+LF line endings!
)
)
del "%~1.temp"
exit /b
:setsize2
set size2=%~z1
exit /b
We're handling the special case of a file without a trailing newline, as well as a file with two LF-terminated newlines, which both lead to an increase of 2 bytes.
Usage:
check-crlf.bat file-i-care-about.txt
So the main thing to remember, at least for a computer programmer working on modern software is that any combination of CR and LF, in sequence needs to be treated as a newline. You will almost never see the 'old' mac, which is CR with no LF - I prefer to ignore its relatively minuscule existence.. I tend to use 1-byte file processing, but that is a personal preference (a preference that pays a dividend in this scenario) Show proficiency as a programmer by making your code resilient to line ending format of text files.

Looking for fast file comparison

My mission is to compare a list of hundreds of files in Windows. In each comparison I have to compare a pair of files.
The files could be binaries or text files (all kinds)
I'm looking for the fastest time to run this. I have to check if the content is the same or not (I have nothing to do with the content itself - I have to report == or != ).
What could be the fastest way to do so? fc.exe? something else?
If fc.exe is the answer, are there any parameters that should accelerate response time?
I'd prefer to use an EXE that's part of standard Windows installation (but it's not a must).
THANK YOU
I'm assuming you want to do a binary comparison.
I would use the following to compare two files:
fc "file1" "file2" /b >nul && echo "file1" == "file2" || "file1" != "file2"
EDIT
If you have many very large files to compare, it may be worth while comparing file sizes before using FC to compare the entire file. I used the same indicator variable so that I could define the actions to take upon result of "same" or "different" just once, without resorting to CALLed subroutines. A CALL is relatively slow.
set "same="
for %%A in ("file1") do for %%B in ("file2") do (
if %%~zA equ %%~zB fc %%A %%B /b >nul && set "same=1"
)
if defined same (
echo "file1" == "file2"
) else (
echo "file1" != "file2"
)
You can get a CRC hex string of each file using a third party command line tool and compare the hex strings.
Depending on how you are comparing sets of files, when using this method then each file only needs to be read once.
#foxidrive answer has merit, especially when the files are big and on the same physical drive which, unless the comparing software slurps big file chunks at a time causes disk thrashing.
Two utils available are
Inbuilt (for windows 10 at least)
C:\Windows\System32\certutil.exe
This has a slightly awkward (enormous) parameter set but works well, does many more useful conversions and can use a large number of hash algorithms (sorry I lost the link to the full list of algorithms available ); it definitely works with sha1 sha256 sha384 md5.
Ex. certutil -hashfile "E:\huge.7z" md5
and the Microsoft utility fciv (md5 generator by default)
Google for "Microsoft File Checksum Integrity Verifier" and download from Microsoft
Ex. fciv bigFile.7z
I know this is an old question, but I haven't seen these two utilities mentioned much... Hope it helps someone.

Resources