Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a little problem, I would like to edit data with the following batch script.
#echo off
set "txt=C:\Users\Desktop\test-batch\input.txt"
set "temp=C:\Users\Desktop\test-batch\output.txt"
for /f "tokens=1-43 delims=; " %%a in (%txt%) do echo %%a;%%ac;%%ad;%%ae;%%af;%%ag;%%ah;%%ai;%%aj;%%ak;%%al;%%am;%%an;%%ao;%%ap;%%aq; > %temp%
input.txt:
1;2;2;1;1;1;1;1;1;1;;;;;;;;;;;;;;;;1;;;1-Trackingnummer;2-Trackingnummer;3-Trackingnummer;4-Trackingnummer;5-Trackingnummer;6-Trackingnummer;7-Trackingnummer;8-Trackingnummer;9-Trackingnummer;10-Trackingnummer;11-Trackingnummer;12-Trackingnummer;13-Trackingnummer;14-Trackingnummer;15-Trackingnummer;
output.txt (column 1 and columns 29-43):
1;1-Trackingnummer;2-Trackingnummer;3-Trackingnummer;4-Trackingnummer;5-Trackingnummer;6-Trackingnummer;7-Trackingnummer;8-Trackingnummer;9-Trackingnummer;10-Trackingnummer;11-Trackingnummer;12-Trackingnummer;13-Trackingnummer;14-Trackingnummer;15-Trackingnummer;
Can someone tell me where the problem is and why it doesn't work?
What follows is a potential solution which doesn't use tokens and delimiters in the same way as your for /f loop. The idea of this is to output tokens 1 and 29-43 of your semicolon delimited lines, (as per your example).
Please don't forget to correct the file paths in lines 2 and 3 before testing it.
#Echo Off&SetLocal EnableExtensions DisableDelayedExpansion
Set "source=C:\Users\Desktop\test-batch\input.txt"
Set "target=C:\Users\Desktop\test-batch\output.txt"
If Exist "%source%" (For %%G In ("%source%")Do If "%%~aG" GEq "d" GoTo :EOF)Else GoTo :EOF
For %%G In ("%target%\..")Do If "%%~aG" Lss "d" GoTo :EOF
For /F "Delims==" %%G In ('Set f[ 2^>NUL')Do Set "%%G="
(For /F Delims^=^ EOL^= %%G In ('Type "%source%"')Do Call :Sub "%%G") 1> "%target%"
GoTo :EOF
:Sub
SetLocal EnableDelayedExpansion
If "%~1" == "" Exit /B
Set "line=%~1"
Set "i=1"
Set "f[!i!]=%line:;="&Set /A i+=1&Set "f[!i!]=%"
Set "out=!f[1]!;"
For /L %%I In (29,1,43)Do Set "out=!out!!f[%%I]!;"
Echo(%out%
EndLocal
Exit /B
Line 5 and 6 are just checks that your source and target are valid. If the source is not an existing file, or target file directory does not exist, the script is closed.
Line 8 ensures that we have no pre-defined variables with names beginning with f[.
Line 10 reads from the source file and passes each line to the :Sub label which performs the token parsing.
The code in the :Sub label splits each token using the delimiter, setting each to an independent variable, (f[#], where # is the token number). The required variables are then joined in a holding variable, (%out%), which is finally echoed to %target%.
In your case the majority of your tokens were a complete range, so I was able to propagate %out% with token 1, (line 19), and all of the range 29..45 on line 20. If your tokens change then you may replace lines 19 and 20 to Set "out=", and e.g. For %%I In (1 29 32 34 35 37 39 43)Do Set "out=!out!!f[%%I]!;" respectively.
Please note that this idea was written to be able to perform the task you laid out in your question. It has a line character length limit and the number of tokens is limited to the allowed size of the environment.
Related
I'm a biologist, with no coding knowledge, trying to create a script that reads every *rprt.txt file in a folder.
In line 11 of each file, the fifth word is a number, If that number is 6000<number<14000 then I want to read the fifth word in line 13 and if that number is greater than 600. Copy the file into another folder in that directory.
At this point I've tried a lot of things. I know the next code is exiting the loop but is the best I got.
#echo off
for %%f in (*rprt.txt) do set "name=%%f" &goto first
:first
for /F "skip=10 tokens=5" %%i in (%name%) do set "var1=%%i" &goto nextline
:nextline
for /F "skip=12 tokens=5" %%i in (%name%) do set "var2=%%i" &goto nextline2
:nextline2
if %var1% geq 6000 (if %var2% geq 600 echo.%name% >> valid.txt)
I've also tried this to test the for loop but I don't understand what's wrong. This prints "echo is off" 3 times
#echo off
for %%f in (*rprt.txt) do (set "name=%%f" & echo %name% >> valid.txt)
#ECHO OFF
SETLOCAL
rem The following settings for the directories and filenames are names
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
FOR %%e IN ("%sourcedir%\*rprt.txt") DO (
rem %%e has filename
SET "line11="
FOR /f "usebackqskip=10tokens=5" %%y IN ("%%e") DO IF NOT DEFINED line11 (
SET "line11=y"
SET "line13="
FOR /f "usebackqskip=12tokens=5" %%o IN ("%%e") DO IF NOT DEFINED line13 (
SET "line13=y"
IF %%y gtr 6000 IF %%y lss 14000 IF %%o gtr 600 ECHO COPY "%%e" "%destdir%"
)
)
)
GOTO :EOF
Always verify against a test directory before applying to real data.
Note that if the filename does not contain separators like spaces, then both usebackq and the quotes around "%%e" can be omitted.
I'm assuming that the values in token 5 of the two lines are guaranteed numeric.
You were definitely on the right track, but the code for validating that something is a number can get kinda weird if you're not used to seeing it (in this case, I remove everything that isn't a digit and then return 1 if there's anything remaining) and the way that GTR and LSS work can also be confusing since it's based on ASCII values so words report as greater than numbers.
The script expects the reports to be in their own folder and the output folder to be in its own folder, and both of these folders should be in the same folder as the script, as opposed to the script being in the same folder as the input files.
#echo off
setlocal enabledelayedexpansion
set "input_directory=%~dp0\input"
set "output_directory=%~dp0\output"
pushd "%input_directory%"
for %%A in (*_rprt.txt) do (
for /f "tokens=5" %%B in ('findstr /n /r "^" "%%~A" ^| findstr "11:"') do set "line_11_num=%%B"
for /f "tokens=5" %%B in ('findstr /n /r "^" "%%~A" ^| findstr "13:"') do set "line_13_num=%%B"
call :isNumber !line_11_num! n[11]
call :isNumber !line_13_num! n[13]
set /a "valid_report=!n[11]!+!n[13]!"
if "!valid_report!"=="0" (
if !line_11_num! GTR 6000 if !line_11_num! LSS 14000 (
if !line_13_num! GTR 600 (
copy "%%~A" "%output_directory%"
)
)
)
)
exit /b
::------------------------------------------------------------------------------
:: Determines if a given string is a positive integer
::
:: Arguments: %1 - The value to check
:: %2 - The variable to store the result in
:: Returns: 0 if the number is a positive integer, 1 otherwise
::------------------------------------------------------------------------------
:isNumber
set "is_number=0"
for /f "delims=0123456789" %%A in ("%~1") do set "is_number=1"
set "%~2=%is_number%"
exit /b
The files and lines processed by for /F command must be processed completelly until the file ends; you can not "cut" the process at the middle with a goto command because the whole process is cancelled.
This means that all lines of all files must be processed with nested for /F commands and you must insert some type of control in order to "omit" the rest of lines that are not the 11 or 13. If the files are numerous or very large, this can take some time.
You can also take just the lines 11 and 13 via findstr commands, but anyway the execution of a couple of findstr commands connected via a pipe also takes some time.
You must be aware that any variable that takes its value inside a compound command (like for or if) must be accessed using !delayedExpansion! instead of %standardExpansion%. There are a lot of questions/answers in this site about this point.
My solution below takes a different approach: it reads just the first 13 lines of each file via a redirection instead of for /F command or findstr. If the files are few and small, this method would be similar in time to the other ones. However, I think this method is simpler and easier to understand.
#echo off
setlocal EnableDelayedExpansion
rem Read every *rprt.txt file in this folder
for %%f in (*rprt.txt) do (
rem Read line 11 and 13 of this file via a redirection
< "%%f" (
rem Skip first 10 lines
for /L %%i in (1,1,10) do set /P "dummy="
rem Read line 11 and line 13
set /P "line11="
set /P "dummy="
set /P "line13="
)
rem Get the number in line 11 and compare it
for /F "tokens=5" %%i in ("!line11!") do set "num=%%i"
if 6000 lss !num! if !num! lss 14000 (
rem Get the number in line 13 and compare it
for /F "tokens=5" %%i in ("!line13!") do set "num=%%i"
if !num! gtr 600 copy "%%f" anotherFolder
)
)
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
I have a variable %data% in a Windows batch file which has variable information in the following format:
URL_A
TEXT
URL_B
So essentially, three strings separated by two newline characters. The length of each string varies on each run. I need to split these three lines into three separate variables. The FOR command seemed promising but there doesn't seem to be a way to specify a newline delimiter for the delim= option.
EDIT (as requested by #Compo):
Here is the batch file's syntax:
play_vid.bat -stream <VID_HEIGHT> <START-TIME> <URL>
Here is the command that generates the %data% variable in my batch file (requires youtube-dl to be installed within the %PATH% environment variable to work):
FOR /F "skip=1 delims=" %%a IN ('youtube-dl --get-title -g -f "bestvideo[height<=%2][fps<=30],worstaudio" --no-playlist "%4"') DO #set data=%%a
Here is a sample command with all the batch variables resolved so that it can be run directly from cmd to test and view the contents of the %data% variable:
FOR /F "skip=1 delims=" %a IN ('youtube-dl --get-title -g -f "bestvideo[height<=1080][fps<=30],worstaudio" --no-playlist "https://www.youtube.com/watch?v=ltynWs_LtIw"') DO #echo %a
For this particular example, %data% will contain:
https://manifest.googlevideo.com/api/manifest/dash/expire/1592941694/ei/HgjyXvilD4ndwQG3nrzoBg/ip/[redacted]
How To Stabilize GoPro Video for Free with FFMPEG
https://manifest.googlevideo.com/api/manifest/dash/expire/1592941694/ei/HgjyXvilD4ndwQG3nrzoBg/ip/[redacted]
that is,
line 1: The URL of the video (DASH) stream.
line 2: The title of the video.
line 3: The URL of the audio (DASH) stream.
The for /F command is intended to loop through lines, so line-breaks as delims option would not make much sense. Anyway, you could combine for /F with if defined:
#echo off
rem // Initially clear variables:
set "ONE=" & set "TWO=" & set "THREE="
rem // Read file line by line (note that `eol=;` by default):
for /F "usebackq delims=" %%L in ("data.txt") do (
if not defined ONE (
rem // This section is entered in the 1st iteration:
set "ONE=%%L"
) else if not defined TWO (
rem // This section is entered in the 2nd iteration:
set "TWO=%%L"
) else (
rem // This section is entered in the 3rd iteration (and further):
set "THREE=%%L"
)
)
echo Line 1: %ONE%
echo Line 2: %TWO%
echo Line 3: %THREE%
As an alternative, you could assign a pseudo-array, like this:
#echo off
rem // Initially clear pseudo-array variables:
for /F "delims== eol==" %%V in ('set $LINE[ 2^> nul') do set "%%V="
rem // Read file line by line, precede each one with line number + `:`:
for /F "tokens=1* delims=:" %%K in ('findstr /N "^" "data.txt"') do (
rem // Assign pseudo-array element with line number as index:
set "$LINE[%%K]=%%L"
rem // Conditionally leave loop (optionally):
if %%K geq 3 goto :NEXT
)
:NEXT
rem // Return result:
set $LINE[
For more information on that topic, refer to this comprehensive answer: Arrays, linked lists and other data structures in cmd.exe (batch) script
I don't know if you mean something like this ?
#echo off
<test.txt (
set /p Var1=
set /p Var2=
set /p Var3=
)
echo Var1=%Var1%
echo Var2=%Var2%
echo Var3=%Var3%
pause
My question is about finding and saving the last word of each line.
I have multiple lines. This information is saved in a .txt file. Thus, I am required to build a CMD script that can scan through the txt file, find the last word of each line and save it to a new .txt document. The delimiters here will be the space.
For example:
It's a very small window<34>.
The small birds were singing softly.
There are three small rooms up stairs (4)
The house has but two small second story bedrooms.
The result I would like to see saved in a separate txt document needs to look like the following:
window<34>.
softly.
(4)
bedrooms.
I have tried many options and methods but unfortunately no luck.
Give this a try. It uses a neat trick with the SET command. You can get a high level overview of this technique at dostips.com.
#echo off & setlocal enableextensions disabledelayedexpansion
FOR /F "delims=" %%G IN (test.txt) DO CALL :lasttoken "%%~G"
GOTO :EOF
:lasttoken
set "x=%~1"
set "x1=%x: =" & set "x2=%"
setlocal enabledelayedexpansion
>>newtest.txt echo !x2!
endlocal
goto :eof
This pure Batch method correctly process all characters, but it is limited in the number of words per line (26 in this version), although such a limit may be increased via the usual tricks.
#echo off
setlocal DisableDelayedExpansion
for /F tokens^=1-26^ eol^= %%a in (input.txt) do (
set "last="
for %%T in (z y x w v u t s r q p o n m l k j i h g f e d c b a) do (
if not defined last call :sub %%T
)
)
goto :EOF
:sub token
for %%? in (-) do (
set "last=%%%1"
if defined last echo %%%1
)
exit /B
Squashman's answer can fail if the file contains quotes and poison characters.
It is possible to write a pure batch solution that reliably does the job, regardless of content.
#echo off
setlocal disableDelayedExpansion
>"output.txt" (
for /f usebackq^ delims^=^ eol^= %%A in ("input.txt") do (
set "s=%%A"
setlocal enableDelayedExpansion
set "s=!s:#=#a!"
set "s=!s:\=#b!"
set "s=!s:/=#f!"
set "s=!s:.=#d!"
set "s=!s: =.!"
for /f "delims=" %%A in (".!s!") do (
endlocal
set "s=%%~xA"
setlocal enableDelayedExpansion
if defined s (
set "s=!s:.= !"
set "s=!s:#d=.!"
set "s=!s:#f=/!"
set "s=!s:#b=\!"
set "s=!s:#a=#!"
echo(!s:~1!
)
endlocal
)
)
)
But I would never use a pure batch solution. Instead I would use JREPL.BAT - a hybrid batch/Jscript utility that efficiently performs regular expression search and replace on text. It isn't pure batch, but it is pure script that runs natively on any Windows machine from XP onward - no 3rd party .exe file required.
call jrepl "(\S+)\s*$" "$txt=$1" /jmatchq /f input.txt /o output.txt
One nice thing about both solutions is that they give the correct result even if there are spaces after the last "word". And they don't print anything if the line does not contain any "word"
I have created command script for reading %N% lines from file. The problem is I can't delete " from anywhere in all text streams when I work with file's text. " deletion is very needed because if file's text line have substring like "text" and text have special chars or even worse, script code, then the script crashes or works not proper way (including script control capturing by programmer who specially composed the text).
If I can't delete " from the text stream(s), then I just want to identify, that the file (or it's first %N% lines, including empty lines) contains at least one " char.
Any thoughts are appreciated, including any file preprocessing. But main aim is script speed.
for /f "skip=2 delims=" %%a in ('find /v /n "" "file" 2^>nul') do set "v=%%a"&call :v&if not errorlevel 1 goto FURTHER1
goto FURTHER2
:v
for /f "delims=[]" %%a in ("%v%") do set "line%%a=%v:*]=%"&if %%a lss %N% (exit /b 1) else exit /b 0
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q39558311.txt"
SET "tempfilename1=%sourcedir%\q39558311#.txt"
>"%tempfilename1%" ECHO("
SET /a linefound=0
FOR /f "tokens=1 delims=:" %%a IN ('findstr /n /g:"%tempfilename1%" "%filename1%"') DO (
IF %%a gtr 2 SET /a linefound=%%a&GOTO report
)
:report
ECHO quote found AT line %linefound%
DEL "%tempfilename1%"
GOTO :EOF
You would need to change the setting of sourcedir and filename1 to suit your circumstances.
tempfile1 can be any name - it's just a temporary file; I chose that particular name for convenience.
I used a file named q39558311.txt containing some dummy data for my testing.
Essentially, create a file containing a single quote on a single line *tempfile1) then use findstr with the /g:filename option to read in the target strings to find. When findstr finds the line, it numbers it and outputs line_number:line found. Using : as a delimiter, token 1 of this line is the line number.
I don't understand why you've used the skip=number in your code. Do you intend to skip testing the first 2 lines of the target file?
the IF %%a gtr 2 tests the line number found. If it is greater than 2, then the variable linefound is set and the for loop is terminated.
I chose to initialise linefound to zero. It will remain zero if no " is found in lines 2..end. Equally, you could clear it and then it will be defined (with a value of first-line-found-with-quote-greater than-2) and no defined on not found.
I can only identify ", but not delete. Waiting for your suggestions on it!
>nul 2>&1 findstr /m \" "file"
if not errorlevel 1 echo double quote found!
I have a file "file.txt" which contains the output of "dir /s /b *.c"
I want to write this whole content of file.txt in a single variable .
Any ideas?
The usual way to treat questions like this one is to reply: "What do you want this for?". However, your question is pure and simple, so here is the answer. The Batch file below not just store the contents of file.txt in a single variable, but it also later process the variable value as individual lines.
EDIT: I added the method to extract individual lines from the variable value as substrings.
#echo off
setlocal EnableDelayedExpansion
rem Create variables with LF and CR values:
set LF=^
%empty line 1/2, don't remove%
%empty line 2/2, don't remove%
for /F %%a in ('copy /Z "%~F0" NUL') do set CR=%%a
rem Store the contents of file.txt in a single variable,
rem end each line with <CR><LF> bytes
set "variable="
for /F "delims=" %%a in (file.txt) do (
set "variable=!variable!%%a!CR!!LF!"
)
rem 1- Show the contents of the variable:
echo !variable!
rem 2- Process the contents of the variable line by line
echo/
set i=0
for /F "delims=" %%a in ("!variable!") do (
set /A i+=1
echo Line !i!- %%a
)
rem Get the starting position and length of each line inside the variable
set /A i=0, lastStart=0
for /F "delims=:" %%a in (
'(cmd /V:ON /C set /P "=^!variable^!" ^& echo/^) ^<NUL ^| findstr /O "^^"'
) do (
set /A len[!i!]=%%a-lastStart-2, i+=1
set /A start[!i!]=%%a, lastStart=%%a
)
set "len[0]="
set "start[%i%]="
set /A lastLine=i-1
rem 3- Extract individual lines from the variable contents as substrings
:getNumber
echo/
set "num="
set /P "num=Enter line number (nothing to end): "
if not defined num goto end
if %num% gtr %lastLine% echo Invalid number & goto getNumber
for /F "tokens=1,2" %%i in ("!start[%num%]! !len[%num%]!") do (
echo Line %num%- !variable:~%%i,%%j!
)
goto getNumber
:end
You must note that Batch variables can only store a maximum of 8K characters.
No. But you can go through the lines one by one.
for /f "delims=" %A in (file.txt) do echo %A
Maybe say what you are trying to achieve. Knowledge of C won't help you in batch because it's contary for historical reasons.
You can use set /p:
set /p foo=<file.txt
See also this question.
Batch-Script is a very limited tool with a primitive support for multi-line variables (with specific hacks that will not work as expected for this sceneario but if you are interested see this).
The only reasonable solution is to move to a capable language, which is every else less Batch-Script.