I write batch script to find contents for file in folder. Contents are in text file and have special characters like exclamation mark.
How do I get FILENAME and FOLDERNAME which contain exclamation mark.
#ECHO off
SETLOCAL EnableDelayedExpansion
set /p SRC="Enter source folder link: "
set /p DST="Enter destination folder link: "
FOR /F "delims=" %%a IN ('DIR /b /s /a-d "%SRC%"') do (
Set "CODE=%%~na"
Set "EXT=%%~xa"
findstr /c:"!CODE!" "%SRC%\Content.txt">nul
IF "!errorlevel!" EQU "0" (
for /F "tokens=2,3" %%c in ('findstr /c:"!CODE!" "%SRC%\Content.txt"') do (
ECHO !CODE!
Set "NEWNAME=%%c"
Set "FOLDERNAME=%%d"
Set "NEWNAME=!NEWNAME:_= !"
Set "FOLDERNAME=!FOLDERNAME:_= !"
IF not exist "%DST%\!FOLDERNAME!" md "%DST%\!FOLDERNAME!"
mklink "%DST%\!FOLDERNAME!\!NEWNAME!!EXT!" "%%a"
)
)
)
Endlocal
Exit
PS: Source folder has many files.
One solution is using a subroutine to avoid usage of delayed environment variable expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
:GetSource
set "SRC="
set /P SRC="Enter source folder link: "
if not defined SRC goto GetSource
set "SRC=%SRC:"=%"
if not defined SRC goto GetSource
:GetDestination
set "DST="
set /P DST="Enter destination folder link: "
if not defined DST goto GetDestination
set "DST=%DST:"=%"
if not defined DST goto GetDestination
for /F "eol=| delims=" %%I in ('dir /A-D /B /S "%SRC%" 2^>nul') do (
if exist "%SRC%\Content.txt" for /F "tokens=2,3" %%A in ('%SystemRoot%\System32\findstr.exe /C:"%%~nI" "%SRC%\Content.txt" 2^>nul') do (
set "NEWNAME=%%~A"
set "FOLDERNAME=%%~B"
call :MakeLink "%%I"
)
)
endlocal
exit /B
:MakeLink
echo %~n1
set "NEWNAME=%NEWNAME:_= %"
set "FOLDERNAME=%FOLDERNAME:_= %"
if not exist "%DST%\%FOLDERNAME%" md "%DST%\%FOLDERNAME%"
mklink "%DST%\%FOLDERNAME%\%NEWNAME%%~x1" %1
goto :EOF
Open a command prompt window and run call /? for help explaining how to use the command CALL with enabled command extensions to run a block in same batch file like a subroutine. See also Where does GOTO :EOF return to?
I have not studied your code, but I'd assume that enabling the delayed expansion after setting the variable names would be more appropriate:
#Echo Off
SetLocal DisableDelayedExpansion
Set /P "SRC=Enter source folder link: "
Set /P "DST=Enter destination folder link: "
For /D /R %%A In (*) Do For /F "Tokens=2-3" %%B In (
'FindStr/C:"%%~nxA" "%SRC%\Content.txt" 2^>Nul') Do (Echo %%~nA
Set "NEW=%%B"
Set "FLD=%%C"
SetLocal EnableDelayedExpansion
If Not Exist "%DST%\!FLD:_= !\" MD "%DST%\!FLD:_= !" 2>Nul && (
MkLink "%DST%\!FLD:_= !\!NEW:_= !%%~xA" "%%A")
Endlocal)
Exit /B
I would strongly suggest you perform some proper verification of the user input prior to performing tasks using them, i.e. before the For loop.
I have some files in the form:
filename1 1.ext
filename1 2.ext
filename1 3.ext
...
filename2 1.ext
filename2 100.ext
...
filename20 1.ext
filename20 15.ext
(etc.)
...where filename can contain spaces.
And I want to move them to folder filename1, filename2, etc., respectively.
I know I can do a for loop for %%i in (*.ext) do and remove the extension with set folder=%%~ni. So what I am missing is how to remove everything after the space just before the number, and get only filename1, for example.
I also know I can split variable folder, but in this case I do not know by at which character I need to split, although I know it will be a space followed by a number.
So basically, I want something like this:
#echo off
set folder=
for %%i in (*.ext) do set folder=%%~ni & set folder=getfoldernamefromvariablefoldersomehow & mv %%i %folder%
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.ext" '
) DO (
CALL :sub1 "%%a" %%a
)
GOTO :EOF
:sub1
SET "filename=%~1"
:subloop
SHIFT
SET "numname=%~1"
IF NOT "%~2"=="" GOTO subloop
CALL SET "dirname=%%filename: %numname%=%%
ECHO( MD "%sourcedir%\%dirname%" 2>nul
ECHO( MOVE "%sourcedir%\%filename%" "%sourcedir%\%dirname%\%numname%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories.
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Perform a directory list of the required files in basic form without directorynames. Send the full fulename in quotes and without to the subroutine sub1.
In the subroutine, save the source filename in filename then shift each parameter supplied until there is no second parameter; the value in numname must then be the last or required filename.
Remove numname with a leading space from filename to get the required subdirectoryname, make that subdirectory and move the file.
[edit in the light of comment]
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.ext" '
) DO (
CALL :sub1 "%%a" %%a
)
GOTO :EOF
:sub1
SET "filename=%~1"
SET "destdirname=%~2"
:subloop
SHIFT
SET "numname=%~1"
IF NOT "%~2"=="" GOTO subloop
CALL SET "dirname=%%filename: %numname%=%%
ECHO( MD "%sourcedir%\%destdirname%" 2>nul
ECHO( MOVE "%sourcedir%\%filename%" "%sourcedir%\%destdirname%\%numname%"
GOTO :eof
It's difficult to scry your intentions when you give no example.
destdirname is set to the second parameter on entering sub1 which will be the first group of characters before the first space.
the md does not need to be gated since the 2>nul will suppress the directory exists error message.
Here is a script that does what you want. It splits off the last SPACE followed by numerals from the file name and uses the remaining string as the name of the destination directory of the movement.
This approach handles all valid characters for file names properly, even ^, &, %, !, ( and ). It can even handle file names that contain SPACE plus numerals plus .ext again correctly.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=."
set "_TARGET=."
for /F "eol=| delims=" %%F in ('
dir /B "%_SOURCE%\*.ext" ^| findstr /R /I /C:" [0123456789][0123456789]*\.ext$"
') do (
set "FILE=%%F"
call :SPLIT LAST REST "%%F"
setlocal EnableDelayedExpansion
2> nul mkdir "!_TARGET!\!REST!"
ECHO move /Y "!_SOURCE!\!FILE!" "!_TARGET!\!REST!"
endlocal
)
endlocal
exit /B
:SPLIT rtn_last rtn_rest val_string
setlocal DisableDelayedExpansion
set "RES=" & set "STR=%~3"
:LOOP
for /F "tokens=1,* delims= " %%I in ("%STR%") do (
if "%%J"=="" (
set "RES=%%I"
) else (
set "STR=%%J"
goto :LOOP
)
)
set "STR=%~3|"
call set "STR=%%STR: %RES%|=%%"
(
endlocal
set "%~1=%RES%"
set "%~2=%STR:^^=^%"
)
exit /B
After having tested the script, remove the upper-case ECHO command to actually move any files. Unless you remove the /Y option from the move command, files become overwritten without prompt. To suppress summary messages (like 1 file(s) moved.), add > nul to the move command line. Note that any prompt was also hidden then in case you removed the /Y option.
Thank to all of you for your comments. Finally, i was able to get a solution:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_source=C:\Users\kurok_000\Downloads"
set "_target=C:\Users\kurok_000\YandexDisk\Mangas"
for /f "eol=| delims=" %%f in ('dir /b "%_source%\*.7z"') do (
call :fixNames "%%f" %_source%
)
for /f "eol=| delims=" %%f in ('dir /b "%_source%\*.7z" ^| findstr /r /i /c:" [0123456789][0123456789]*\.7z$"') do (
set "file=%%f"
call :split last rest "%%f"
setlocal EnableDelayedExpansion
2> nul mkdir "!_target!\!rest!"
move /y "!_source!\!file!" "!_target!\!rest!" >nul
echo moved %%f to !_target!\!rest!
endlocal
)
endlocal
exit /b
:split rtn_last rtn_rest val_string
setlocal DisableDelayedExpansion
set "res=" & set "str=%~3"
:loop
for /f "tokens=1,* delims= " %%i in ("%str%") do (
if "%%j"=="" (
set "res=%%i"
) else (
set "str=%%j"
goto :loop
)
)
:quit
set "str=%~3|"
call set "str=%%str: %res%|=%%"
(
endlocal
set "%~1=%res%"
set "%~2=%str:^^=^%"
)
exit /b
:fixNames _file _folder
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=%1"
set "FILE=%file:~1,-1%"
set "folder=%2"
for /F "tokens=1,* delims=0123456789" %%A in ("%FILE%") do (
set filename=!FILE:%%B=!%%~xB
)
if not "%filename%"=="%FILE%" (rename "!folder!\!FILE!" "!filename!")
This is my first post and I am not very skilled with batch so sorry if I really mess up.
Basically I'm working on a little batch script where once run, the user inputs a file path and line number and the specified line of the specified file will be output to the command line. I have all my variables and commands working, and the command to specify the line of the text file works fine, its just when I put my variables in it doesn't work. Now I'm guessing what I'm doing is obviously wrong since I'm new to batch, but anyway here's my code:
#echo off
color b
:top
title specified line copy tool
echo input full path to txt file
set /P filepath=">"
cls
echo what line would you like to copy?
set /P lineoriginal=">"
set /A actualline=%lineoriginal%-1
for /F "skip=%actualline% delims=" %%i in (%filepath%) do if not defined output set "output=%%i"
echo %output%
pause
See if you can see what I did wrong, thanks.
From this link Windows Batch file to echo a specific line number
you can call this function like that : Call:ReadNthLine <File> <nLine>
:ReadNthLine File nLine
FOR /F %%A IN ('^<"%~1" FIND /C /V ""') DO IF %2 GTR %%A (ECHO Error: No such line %2. 1>&2 & EXIT /b 1)
FOR /F "tokens=1* delims=]" %%A IN ('^<"%~1" FIND /N /V "" ^| FINDSTR /B /C:"[%2]"') DO ECHO.%%B
EXIT /b
And your code can be re-written like that :
#echo off
color b
:top
cls
title specified line copy tool
echo input full path to txt file
set /P "filepath=>"
cls
echo what line would you like to copy ?
set /P "nLine=>"
cls
CALL :ReadNthLine "%filepath%" %nLine%
PAUSE >NUL & goto:top
GOTO :EOF
::***************************************************************************************
:ReadNthLine File nLine
FOR /F %%A IN ('^<"%~1" FIND /C /V ""') DO IF %2 GTR %%A (ECHO Error: No such line %2. 1>&2 & EXIT /b 1)
FOR /F "tokens=1* delims=]" %%A IN ('^<"%~1" FIND /N /V "" ^| FINDSTR /B /C:"[%2]"') DO ECHO.%%B
EXIT /b
::***************************************************************************************
I want to count the number of lines of each text file in a given directory and store them in a variable.
Here is my code:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "temp\textpipe_tmp\" %%U in (*.txt) DO (
set "cmd=findstr /R /N "^^" "%%U" | find /C ":""
for /f %%a in ('!cmd!') do set number=%%a
echo %number%
)
:eof
pause
I'm not sure why it does not work but if I get rid of SET, it works:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "temp\textpipe_tmp\" %%U in (*.txt) DO (
findstr /R /N "^" "%%U" | find /C ":"
)
:eof
pause
I need the result stored in a variable.
Another version, which does the same thing but is slightly better readable:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "C:\Users\Gebruiker\Documents\ICT" %%U in (*.txt) DO (
set lines=0
for /f %%A in (%%U) do (set /a lines+=1)
echo !lines!
)
pause
As #wOxxOm stated in his comment, find is the perfect choice for this task.
Supposing there is a file test.txt containing 12 lines, find /V /C "" "C:test.txt" will output something like:
---------- C:TEST.TXT: 12
So let us use a for /F loop to capture such an output and string substitution to get the text portion after :SPACE:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /R "temp\textpipe_tmp\" %%U in ("*.txt") do (
rem capturing the output of `find` here:
for /F "delims=" %%A in ('find /V /C "" "%%~U"') do (
set "NUMBER=%%~A"
rem substituting substring `: ` and everything before by nothing:
set "NUMBER=!NUMBER:*: =!"
)
rem at this point, variable `NUMBER` is available
rem for the currently processed file in `%%~U`:
echo !NUMBER!
)
endlocal
Note that find /V /C "" will return unexpected reslts if there are empty lines at the end of the file (one of such might not be included in the count). However, empty lines at the beginning or in between non-empty ones will be counted.
Update:
Using redirection like > "C:test.txt" find /V /C "" rather than find /V /C "" "C:test.txt" avoids the prefix ---------- C:TEST.TXT: and just returns the number of lines (like 12). With this modification no string substitution is necessary and so the code looks like this:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /R "temp\textpipe_tmp\" %%U in ("*.txt") do (
rem capturing the output of `find` here:
for /F "delims=" %%A in ('^> "%%~U" find /V /C ""') do (
set "NUMBER=%%~A"
)
rem at this point, variable `NUMBER` is available
rem for the currently processed file in `%%~U`:
echo !NUMBER!
)
endlocal
The redirection mark < needs to be escaped like ^< when being used after in in for /F.
I want to count the no of lines in a text file and then the value has to be stored into a environment variable. The command to count the no of lines is
findstr /R /N "^" file.txt | find /C ":"
I refered the question How to store the result of a command expression in a variable using bat scripts?
Then I tried,
set cmd="findstr /R /N "^" file.txt | find /C ":" "
I am getting the error message,
FIND: Parameter format not correct
How could i get rid of this error.
There is a much simpler way than all of these other methods.
find /v /c "" filename.ext
Holdover from the legacy MS-DOS days, apparently. More info here: https://devblogs.microsoft.com/oldnewthing/20110825-00/?p=9803
Example use:
adb shell pm list packages | find /v /c ""
If your android device is connected to your PC and you have the android SDK on your path, this prints out the number of apps installed on your device.
You could use the FOR /F loop, to assign the output to a variable.
I use the cmd-variable, so it's not neccessary to escape the pipe or other characters in the cmd-string, as the delayed expansion passes the string "unchanged" to the FOR-Loop.
#echo off
cls
setlocal EnableDelayedExpansion
set "cmd=findstr /R /N "^^" file.txt | find /C ":""
for /f %%a in ('!cmd!') do set number=%%a
echo %number%
Inspired by the previous posts,
a shorter way of doing so:
CMD.exe
C:\>FINDSTR /R /N "^.*$" file.txt | FIND /C ":"
The number of lines
Try it. It works in my console.
EDITED:
(the "$" sign removed)
FINDSTR /R /N "^.*" file.txt | FIND /C ":"
$ reduces the number by 1 because it is accepting the first row as Field name and then counting the number of rows.
Try this:
#Echo off
Set _File=file.txt
Set /a _Lines=0
For /f %%j in ('Find "" /v /c ^< %_File%') Do Set /a _Lines=%%j
Echo %_File% has %_Lines% lines.
It eliminates the extra FindStr and doesn't need expansion.
- edited to use ChrisJJ's redirect suggestion. Removal of the TYPE command makes it three times faster.
#Tony: You can even get rid of the type %file% command.
for /f "tokens=2 delims=:" %%a in ('find /c /v "" %_file%') do set /a _Lines=%%a
For long files this should be even quicker.
I usually use something more like this
for /f %%a in (%_file%) do (set /a Lines+=1)
for /f "usebackq" %A in (`TYPE c:\temp\file.txt ^| find /v /c "" `) do set numlines=%A
in a batch file, use %%A instead of %A
The perfect solution is:
FOR /F %%i IN ('TYPE "Text file.txt" ^| FIND /C /V ""') DO SET Lines=%%i
I found this solution to work best for creating a log file that maintains itself:
setlocal enabledelayedexpansion
SET /A maxlines= 10
set "cmd=findstr /R /N "^^" "filename.txt" | find /C ":""
for /f %%a in ('!cmd!') do set linecount=%%a
GOTO NEXT
:NEXT
FOR /F %%A IN ("filename.txt") DO (
IF %linecount% GEQ %maxlines% GOTO ExitLoop
echo %clientname% %Date% %Time% >> "filename.txt")
EXIT
:ExitLoop
echo %clientname% %Date% %Time% > "filename.txt"
EXIT
Environmental variables included are %clientname% the computername of the remote client %Date% is the current date and %Time% the current time. :NEXT is called after getting the number of lines in the file. If the file line count is greater than the %maxlines% variable it goes to the :EXITLOOP where it overwrites the file, creating a new one with the first line of information. if it is less than the %maxlines% variable it simply adds the line to the current file.
You don't need to use find.
#echo off
set /a counter=0
for /f %%a in (filename) do set /a counter+=1
echo Number of lines: %counter%
This iterates all lines in the file and increases the counter variable by 1 for each line.
The :countLines subroutine below accepts two parameters: a variable name; and a filename. The number of lines in the file are counted, the result is stored in the variable, and the result is passed back to the main program.
The code has the following features:
Reads files with Windows or Unix line endings.
Handles Unicode as well as ANSI/ASCII text files.
Copes with extremely long lines.
Isn’t fazed by the null character.
Raises an error on reading an empty file.
Counts beyond the Batch max int limit of (31^2)-1.
#echo off & setLocal enableExtensions disableDelayedExpansion
call :countLines noOfLines "%~1" || (
>&2 echo(file "%~nx1" is empty & goto end
) %= cond exec =%
echo(file "%~nx1" has %noOfLines% line(s)
:end - exit program with appropriate errorLevel
endLocal & goto :EOF
:countLines result= "%file%"
:: counts the number of lines in a file
setLocal disableDelayedExpansion
(set "lc=0" & call)
for /f "delims=:" %%N in ('
cmd /d /a /c type "%~2" ^^^& ^<nul set /p "=#" ^| (^
2^>nul findStr /n "^" ^&^& echo(^) ^| ^
findStr /blv 1: ^| 2^>nul findStr /lnxc:" "
') do (set "lc=%%N" & call;) %= for /f =%
endlocal & set "%1=%lc%"
exit /b %errorLevel% %= countLines =%
I know it looks hideous, but it covers most edge-cases and is surprisingly fast.
Just:
c:\>(for /r %f in (*.java) do #type %f ) | find /c /v ""
Font: https://superuser.com/questions/959036/what-is-the-windows-equivalent-of-wc-l
One nice surprise is for one who has git bash on his windows: just plain old linux wc -l <filename> will works for you there
In the below code, the variable name are SalaryCount and TaxCount
#ECHO OFF
echo Process started, please wait...
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Salary.txt"') do set SalaryCount=%%C
echo Salary,%SalaryCount%
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Tax.txt"') do set TaxCount=%%C
echo Tax,%TaxCount%
Now if you need to output these values to a csv file, you could use the below code.
#ECHO OFF
cd "D:\CSVOutputPath\"
echo Process started, please wait...
echo FILENAME,FILECOUNT> SUMMARY.csv
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Salary.txt"') do set Count=%%C
echo Salary,%Count%>> SUMMARY.csv
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Tax.txt"') do set Count=%%C
echo Tax,%Count%>> SUMMARY.csv
The > will overwrite the existing content of the file and the >> will append the new data to existing data. The CSV will be generated in D:\CSVOutputPath
You can pipe the output of type into find inside the in(…) clause of a for /f loop:
for /f %%A in ('
type "%~dpf1" ^| find /c /v ""
') do set "lineCount=%%A"
But the pipe starts a subshell, which slows things down.
Or, you could redirect input from the file into find like so:
for /f %%A in ('
find /c /v "" ^< "%~dpf1"
') do set "lineCount=%%A"
But this approach will give you an answer 1 less than the actual number of lines if the file ends with one or more blank lines, as teased out by the late foxidrive in counting lines in a file.
And then again, you could always try:
find /c /v "" example.txt
The trouble is, the output from the above command looks like this:
---------- EXAMPLE.TXT: 511
You could split the string on the colon to get the count, but there might be more than one colon if the filename had a full path.
Here’s my take on that problem:
for /f "delims=" %%A in ('
find /c /v "" "%~1"
') do for %%B in (%%A) do set "lineCount=%%B"
This will always store the count in the variable.
Just one last little problem… find treats null characters as newlines. So if sneaky nulls crept into your text file, or if you want to count the lines in a Unicode file, this answer isn’t for you.
You can also try
set n=0 & for /f "tokens=*" %a in (text.txt) do set/a n=!n!+1
echo !n!
You can also mark with a wildcard symbol * to facilitate group files to count.
Z:\SQLData>find /c /v "" FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_*.txt
Result
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_AVIFRS01_V1.TXT: 2041
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_AVIOST00_V1.TXT: 315938
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_AVIFRS00_V1.TXT: 0
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_CNTPTF00_V1.TXT: 277