windows script to list unused #define constant definitions in a C project - c-preprocessor

I am a newbie to creating scripts. I am working in a C language based software project. One of the problems is that there are a lot of unused #define constant definitions in the project and I have created a windows script to find the unused #defines as given below:
rem /*Extract all the lines with #defines to log.txt */
findstr "#define" *.h >log.txt
rem /*Extract all the 'tokens' from log.txt to log2.txt */
FOR /F "tokens=2 delims= " %%G IN (log.txt) DO #echo %%G >>log2.txt
rem /*Search through the *.c files in the folder to see if the */
rem /*tokens from log2.txt are used. If not, print them to log5.txt */
for /F %%i in (log2.txt) do (
findstr /M /C:%%i *.c
If ERRORLEVEL 1 echo %%i >>log5.txt )
The problem with the above script is that it does not check whether the pre-processor constant is actually used in a source code line or in a Comment line (/* */). Please can someone help? Also, I appreciate any suggestions to simplify the script or point out any drawbacks.

Related

Batch script to remove "duplicate" files

I have a list of files named:
file.txt
file (1).txt
file (2).txt
etc.
Where the greater (number) is the last file updated.
I want a .bat script that allows get the content of file (maxnumer).txt to file.txt.
dir /B /OD /TW file*.txt lists file names in sort order of last written time (cf. dir /?).
Next commented batch script could do the job for you:
#echo OFF
SETLOCAL EnableExtensions
rem delete empty `_lastfile` variable
set "_lastfile="
rem store file name of last written time into `_lastfile` variable
for /F "delims=" %%G in ('dir /B /OD /TW file*.txt 2^>NUL') do set "_lastfile=%%~G"
rem ↑↑↑↑↑↑ suppress errors
rem check the `_lastfile` variable
rem is defined?
rem AND is different from "file.txt"?
if defined _lastfile if /I "%_lastfile%" NEQ "file.txt" copy /Y "%_lastfile%" "file.txt"
Resources (required reading):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(%~G etc. special page) Command Line arguments (Parameters)
(2>NUL etc. special page) Redirection
Here is a bit of a hacky answer.
This script will move all files from file (1).txt up to file (10).txt to file.txt , leaving only file.txt which now contains the text that was in file (10).txt
Doing it in ascending order will ensure the highest number that exists is the last one to be moved.
#echo off
set /P name=Enter name of file without extension:
echo enter extension:
set /P ext=.
echo. & echo.
echo these actions will be performed: & echo.
FOR /L %%A IN (1,1,10) DO #echo move /y "%name% (%%A).%ext%" "%name%.%ext%"
echo. & pause & echo.
FOR /L %%A IN (1,1,10) DO move /y "%name% (%%A).%ext%" "%name%.%ext%"
pause
You could use IF EXIST %name% (%%A).%ext% To stop the script from trying to move files that don't exist, but it doesn't really affect anything to do that, so I didn't bother.
The script above will do it for a specific named file. To do it for all files in a directory will be possible, here are some hints to get you going
use a dir /b >filenames.txt to get all files in a directory listed in a text file
to perform an action for every line in a textfile do
for /f "usebackq delims= tokens=* %%a in (`filenames.txt`)" DO (
::some stuff here
)
The way I would go about it would be to get filenames.txt , manually delete all the (n) files so you just have a list of the "non-duplicate" filenames, and use that as your input. You
There are probably more elegant solutions out there, but with all the peculiarities of batch I wouldn't be surprised to find they are pages and pages long.
If you want to keep all the original files not just end up with the final file.txt with no copies, then I you want to use COPY
If you want to keep all the original files, then you would want to use COPY not MOVE.
In that case, to remove all superfluous operations (i.e. only copy the highest numbered file, not copy all the files in order) then something like IF NOT EXIST %name% (!B!).%ext% where !B!=%%A+1 within your (now multiline) for loop and use Setlocal EnableDelayedExpansion on to make the arithmetic work properly. But it's not really necessary, copying 1, then 2, then 3, then 4 does the same thing if a little slower than skipping 1 2 and 3 and just doing 4.
I hope this helps enough to point you in the right direction, feel free to ask questions if this isn't clear.

How to find *.css files for further processing but ignore all *.min.css files?

I want to use yui compressor to automatically and recursively compress my js/css files with a command similar to the following:
for %%f in (*.css) do (
java -jar d:\projects\yuicompressor-2.4.8.jar -o %%~nf.min.css --charset utf8 --nomunge %%~nf.css
)
However, the problem is, if I use loop above, when I update my css/js files later and run this script again, a lot of files ending with .min.min.css and .min.min.js will be produced.
How to find *.css files for compression with ignoring all *.min.css files?
Edit:
after following #Magoo's instructions for recursive version
D:\bak\static>compress.bat
java.io.FileNotFoundException: bakstaticstyleSet.min.css:\bak\static\styleSet.cs
s (文件名、目录名或卷标语法不正确。)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(Unknown Source)
at java.io.FileOutputStream.<init>(Unknown Source)
at com.yahoo.platform.yui.compressor.YUICompressor.main(YUICompressor.ja
va:208)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.yahoo.platform.yui.compressor.Bootstrap.main(Bootstrap.java:21)
The Chinese sentence above means incorrect filename, directory name, or volumn name.
This seems to be a bug of yui compressor, rather than the batch scripts. But is there any change that I can do the recursion in a waylike cd to a directory, do something, and then cd .. back.
Edit2:
I run the modified script, it does compress all css files recursively. However, some files generated have wrong names. Examples:
check_input.js ---> CHECK_~1.min.js
jsAddress.js ---> JSADDR~1.min.js
#ECHO OFF
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%f IN (
'dir /b /a-d "%sourcedir%\*.css" ^|findstr /v /i /L /c:".min.css"'
) DO (
ECHO(java -jar d:\projects\yuicompressor-2.4.8.jar -o %%~nf.min.css --charset utf8 --nomunge %%~nf.css
)
GOTO :EOF
This should cure your problem.
You would need to change the setting of sourcedir to suit your circumstances.
The java command is merely echoed - remove the echo( after verification to activate.
#ECHO OFF
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%f IN (
'dir /s /b /a-d "%sourcedir%\*.css" ^|findstr /v /i /L /c:".min.css"'
) DO (
ECHO(java -jar d:\projects\yuicompressor-2.4.8.jar -o "%%~dpnf.min.css" --charset utf8 --nomunge "%%~dpnf.css"
)
GOTO :EOF
Revision for recursion. Assumes java understands quoted-filenames. The quotes are not required if the path/filenames do not contain separators (like Space for instance)
#ECHO OFF
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%f IN (
'dir /s /b /a-d "%sourcedir%\*.css" ^|findstr /v /i /L /c:".min.css"'
) DO (
PUSHD "%%~sdpf"
ECHO !cd!
ECHO(java -jar d:\projects\yuicompressor-2.4.8.jar -o "%%~snf.min.css" --charset utf8 --nomunge "%%~snf.css"
popd
)
GOTO :EOF
Revision to attempt to solve problem with recursion.
I strongly suspect that the problem is related to unicode which cmd doesn't handle well.
This version uses a pushd/popd frame to change the current directory to the curent target. I can't test unicode, so perhaps using the shortname (invoked by the ~s operator) may be of assistance.
I included the echo !cd! simply to display the current directory, gratuitously using the setlocal enabledelayedexpansion that I'd erroneously left in the original batch. I wasn't using it (it's part of my batch template) and it can be omitted (but must be there to use the !var! expansion syntax)
Supplemental information which I hope is useful.
By defauly, every file and directory has two names - the "long" and the "short" name. The "short" name is generated from the "long" name using an algorithm that isn't relevant, but what it aims to do is create an alternative name following the original DOS 8.3 naming scheme - 8 ANSI characters for the name and 3 for the extension, separated by a dot. Naturally, if the name already complied with the 8.3 convention, no extra name was created.
You can see these name by executing dir /x from the prompt.
In batch, the FOR loop can extract parts of a filename. if %%a is the metavariable (the variable controlling the loop) then %%~pa will produce the directory ("Path) part only. Similarly, D is used for the Drive, N is used for the Name, X is used for the eXtension and there are others. See FOR /?|more from the prompt for more docco.
To access the short name - which is designed to be guaranteed-ANSI-8.3 - you use the ~s modifier, so %%~sna means the Short Name of the file "%%a".
Unicode is a later invention. I was hoping that using the short names would allow cmd to process the files as it wasn't really designed for unicode.
Since the name of the generated file is "%%~snf.css" then this means the short name from %%f + ".css", and the pushd/popd frame actually changes the current directory to the real directory by using its short name. Therefore, we hane no need of a directory name when generating the new file (it therefore defaults to the current directory) and the name of the file is the short name because the destination filespec includes the s modifier. Removing the s and setting it to "%%~nf.css" would thus use the original long name - and that's probably safe if the original filename is not unicode.

Modify version number defined in .c file from batch file

I am writing a batch file to start doing automatic firmware builds.
Currently I have it getting the code from TFS (Team Foundation Server) and then building it (the tool is eclipse based).
However, before I build the code, I want to increment the version number, which is stored in a file version.h. The contents of the file are:
/* $Header: /NG EM Controller Firmware Eclipse based/APPS/EM MAIN/version.h 2 4/25/13 10:19a user $
*/
#ifndef Version_h
#define Version_h
#define DSP_FW_VERSION 500
#define DSP_FW_ID 22
#define DESTINATION 1
#endif
/*
* More comments, the number increases with each check in*./
The line #define DSP_FW_VERSION 500 is the line I need to increment (in this case to 501).
I'm very new to batch files and am learning as I go, but this has me stumped. I would prefer not to have to copy each line and modify the one I want, I only want to manipulate the original.
Thanks for any help or guidance.
try this (pure batch):
#ECHO OFF &SETLOCAL ENABLEDELAYEDEXPANSION
SET "HFile=file"
SET "search=#define DSP_FW_VERSION"
FOR /f %%a IN ('^<"%HFile%" find /c /v ""') DO SET /a lines=%%a
< "%HFile%" (
FOR /l %%a IN (1,1,%lines%) DO (
SET "line="
SET /p "line="
IF NOT "!line!"=="" IF NOT "!line:%search%=!"=="!line!" (
SET /a replace=!line:%search%=!+1
SET "line=%search% !replace!"
)
ECHO(!line!
))>"%HFile%.new"
MOVE /y "%HFile%.new" "%HFile%"
TYPE "%HFile%"
A portable way in a perl one-liner (only one process):
perl -i -pe '
BEGIN{$re = qr!^(/\*\s+\$Header:.*?version\.h\s+)(\d+)!};
$re and s#$re#sprintf "%s%d", $1, $2+1#e
' file.h
Remove -i switch if you just want to try it a bit.
#ECHO OFF
SETLOCAL
SET "sourcedir=."
SET targetfile=version.h
SET newversion=%1
IF NOT DEFINED newversion ECHO require new version number as parameter&GOTO :EOF
IF NOT EXIST "%sourcedir%\%targetfile%" ECHO %targetfile% not found&GOTO :eof
DEL "%sourcedir%\%targetfile%_before_%newversion%" >NUL
ren "%sourcedir%\%targetfile%" "%targetfile%_before_%newversion%"
(
FOR /f "delims=" %%a IN (
' FINDSTR /n /R "$" "%sourcedir%\%targetfile%_before_%newversion%" '
) DO (
SET "line=%%a"
SETLOCAL ENABLEDELAYEDEXPANSION
SET line=!line:*:=!
IF "!line:~0,23!"=="#define DSP_FW_VERSION " SET line=!line:~0,23!%newversion%
ECHO(!line!
endlocal
)
)>"%sourcedir%\%targetfile%"
FC "%sourcedir%\%targetfile%" "%sourcedir%\%targetfile%_before_%newversion%"
GOTO :EOF
The above batch is intended to be run with a parameter of the new version number.
It should rename the existing version.h to version.h_before_newversionnumber, and regenerate the version.h file, including empty line, with the one exception of replacing the target line.
The FC command at the end is intended merely to compare the before and after version of the file during your vigorous pre-production testing...
line=`grep -E '#define DSP_FW_VERSION' version.h`
fst=$[line% *}
snd=${line##* }
nvno=`echo "${snd##* } + 1" | bc`
sed "s/$line/$fst $nvno/" version.h
The first line finds the line you want to modify. You may want to play defensive and make sure there is only one line found. The second line splits the beginning of the line, the third the last part into two variables. The fourth line increments your version number using 'bc'. The last one makes the change to version.h by reassembling the first part and the incremented second part. A bit ugly but it should work in bash (you need cygwin).
Code for awk:
awk "$2 ~ /DSP_FW_VERSION/ {$3++};1" file
awk for Windows

This Supybot for windows batch install script needs to create another batch file

This Supybot for windows batch install script needs to create another batch file...
The Problem:
(1) I have a directory that has a file that ends with .conf
(2) There is only one file in this directory that ends with .conf
(3) But I don't know what this file starts with.. all I know is ????????.conf
(4) How do I set the filename.conf and remove the .conf part of the file name?
(5) As it is just the beginning of the filename that I need.
Example:
C:\runbot>find "supybot.ident: " | type *.conf > temp.txt
robotbot.conf
Outputs : robotbot.conf
The quest, is how do I set a variable=robotbot
=========================================================================
The Input was this file named "RootCode.conf" among many others
within the directory searched:
RootCode.conf
The Solution is:
FOR /F "tokens=1,2 delims=." %%a in ('FINDSTR /M "supybot.ident:" *.conf') DO SET USER=%%a&set dontneed=%%b
echo %USER%
pause
The Output is:
C:\runbot>FOR /F "tokens=1,2 delims=." %a in ('FINDSTR /M "supybot.ident:" *.con
f') DO SET USER=%a & set dontneed=%b
C:\runbot>SET USER=RootCode & set dontneed=conf
C:\runbot>echo RootCode
RootCode
C:\runbot>pause
Press any key to continue . . .
Winner... Special thanks Everyone
Your example of piping the output to typecommand is either wrong or useless. So I am assuming you mistyped and the real line was piping the other way around, and thus I am assuming that you are trying to find the filename of the file that contains the string "supybot.ident: ". In that case I would suggest to use findstr command instead.
FOR /F "tokens=*" %%a in ('FINDSTR /M "supybot.ident:" *.conf') DO SET USER=%%a
See HELP FINDSTR, HELP SET and HELP FOR for more information.
It's a bit unclear (to me, at least) what exactly you ask here. But if you need the output of a command, then use for /f:
for /f "delims=" %%x in ('some command ^| line') do set somevar=%%x
Note that you need to escape shell metacharacters in the command line (as they need to survive one parsing pass). Also note that you cannot set a variable to contain more than one line of text.

Windows Batch help in setting a variable from command output [duplicate]

This question already has answers here:
Set output of a command as a variable (with pipes) [duplicate]
(6 answers)
Closed 7 years ago.
I need to run a simple find command and redirect the output to a variable in a Windows Batch File.
I have tried this:
set file=ls|find ".txt"
echo %file%
But it does not work.
If I run this command it works without problems:
set file=test.txt
echo %file%
So obviously my command output is not being set to my variable. Can anyone help? Thanks
I just find out how to use commands with pipes in it, here's my command (that extracts the head revision of an svn repo) :
SET SVN_INFO_CMD=svn info http://mySvnRepo/MyProjects
FOR /f "tokens=1 delims=" %%i IN ('%SVN_INFO_CMD% ^| find "Revision"') DO echo %%i
First of all, what you seem to expect from your question isn't even possible in UNIX shells. How should the shell know that ls|find foo is a command and test.txt is not? What to execute here? That's why UNIX shells have the backtick for such things. Anyway, I digress.
You can't set environment variables to multi-line strings from the shell. So we now have a problem because the output of ls wouldn't quite fit.
What you really want here, though, is a list of all text files, right? Depending on what you need it's very easy to do. The main part in all of these examples is the for loop, iterating over a set of files.
If you just need to do an action for every text file:
for %%i in (*.txt) do echo Doing something with "%%i"
This even works for file names with spaces and it won't erroneously catch files that just have a .txt in the middle of their name, such as foo.txt.bar. Just to point out that your approach isn't as pretty as you'd like it to be.
Anyway, if you want a list of files you can use a little trick to create arrays, or something like that:
setlocal enabledelayedexpansion
set N=0
for %%i in (*.txt) do (
set Files[!N!]=%%i
set /a N+=1
)
After this you will have a number of environment variables, named Files[0], Files[1], etc. each one containing a single file name. You can loop over that with
for /l %%x in (1,1,%N%) do echo.!Files[%%x]!
(Note that we output a superfluous new line here, we could remove that but takes one more line of code :-))
Then you can build a really long line of file names, if you wish. You might recognize the pattern:
setlocal enabledelayedexpansion
set Files=
for %%i in (*.txt) do set Files=!Files! "%%i"
Now we have a really long line with file names. Use it for whatever you wish. This is sometimes handy for passing a bunch of files to another program.
Keep in mind though, that the maximum line length for batch files is around 8190 characters. So that puts a limit on the number of things you can have in a single line. And yes, enumerating a whole bunch of files in a single line might overflow here.
Back to the original point, that batch files have no way of capturing a command output. Others have noted it before. You can use for /f for this purpose:
for /f %%i in ('dir /b') do ...
This will iterate over the lines returned by the command, tokenizing them along the way. Not quite as handy maybe as backticks but close enough and sufficient for most puposes.
By default the tokens are broken up at whitespace, so if you got a file name "Foo bar" then suddenly you would have only "Foo" in %%i and "bar" in %%j. It can be confusing and such things are the main reason why you don't ever want to use for /f just to get a file listing.
You can also use backticks instead of apostrophes if that clashes with some program arguments:
for /f "usebackq" %%i in (`echo I can write 'apostrophes'`) do ...
Note that this also tokenizes. There are some more options you can give. They are detailed in the help for command.
set command has /p option that tells it to read a value from standard input. Unfortunately, it does not support piping into it, but it supports reading a value from a first line of existing file.
So, to set your variable to the name of a first *.txt file, you could do the following:
dir /b *.txt > filename.tmp
set /p file=< filename.tmp
del /q filename.tmp
It is important not to add a space before or even after =.
P. S. No fors, no tokens.
Here's a batch file which will return the last item output by find:
#echo off
ls | find ".txt" > %temp%\temp.txt
for /f %%i in (%temp%\temp.txt) do set file=%%i
del %temp%\temp.txt
echo %file%
for has a syntax for parsing command output, for /f "usebackq", but it cannot handle pipes in the command, so I've redirected output to a temporary location.
I strongly recommend, given that you have access to ls, that you consider using a better batch language, such as bash or even an scripting language like python or ruby. Even bash would be a 20x improvement over cmd scripting.
The short answer is: Don't!
A windows shell env var can hold a max of 32 Kb and it isn't safe to save output from programs in them.
That's why you can't. In batch script you must adopt another programming style. If you need all of the output
from the program then save it to file. If you only need to check for certain properties then pipe the output into
a program that does the checking and use the errorlevel mechanism:
#echo off
type somefile.txt | find "somestring" >nul
if %errorlevel% EQU 1 echo Sorry, not found!
REM Alternatively:
if errorlevel 1 echo Sorry, not found!
However, it's more elegant to use the logical operators Perl style:
#echo off
(type somefile.txt | find "somestring" >nul) || echo Sorry, not found!
It's not available in DOS, but in the Windows console, there is the for command. Just type 'help for' at a command prompt to see all of the options. To set a single variable you can use this:
for /f %%i in ('find .txt') do set file=%%i
Note this will only work for the first line returned from 'find .txt' because windows only expands variable once by default. You'll have to enable delayed expansion as shown here.
what you are essentially doing is listing out .txt files. With that, you can use a for loop to over dir cmd
eg
for /f "tokens=*" %%i in ('dir /b *.txt') do set file=%%i
or if you prefer using your ls, there's no need to pipe to find.
for /f "tokens=*" %%i in ('ls *.txt') do set file=%%i
Example of setting a variable from command output:
FOR /F "usebackq" %%Z IN ( `C:\cygwin\bin\cygpath "C:\scripts\sample.sh"` ) DO SET BASH_SCRIPT=%%Z
c:\cygwin\bin\bash -c '. ~/.bashrc ; %BASH_SCRIPT%'
Also, note that if you want to test out the FOR command in a DOS shell, then you need only use %Z instead of %%Z, otherwise it will complain with the following error:
%%Z was unexpected at this time.

Resources