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
Related
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.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I need to substitute tab with 2 spaces in all files on a specified path using windows batch script. I see that it is not easy to enter tab character correctly into a batch file. Can anyone help. Thanks
EDIT Corrected output of leading spaces.
The tricky part is to grab the tab char. May be...
#echo off
setlocal EnableDelayedExpansion
rem Grab TAB character
rem http://www.dostips.com/forum/viewtopic.php?f=3&t=7898
set "TAB="
rem First, try the method for Windows XP
for /F "skip=4 delims=pR tokens=2" %%a in ('reg query hkcu\environment /v temp' ) do set "TAB=%%a"
rem Then, the method for newer versions
rem http://www.dostips.com/forum/viewtopic.php?f=3&t=1733&p=6840#p6853
for /F "tokens=2 delims=0" %%a in ('shutdown /? ^| findstr /BC:E') do if not defined TAB set "TAB=%%a"
rem set desired number of spaces
set "SPACES= "
pushd "your_desired_path"
for /f "delims=" %%f in ('dir /B *.txt') do (
> "output_%%~nxf" (
for /f "tokens=* delims=" %%i in (%%~nxf) do (
set "myData=%%i"
set "myData=!myData:%TAB%=%SPACES%!"
echo !myData!
)
)
echo copy /y "output_%%~nxf" "%%~nxf"
echo del "output_%%~nxf" >NUL 2>NUL
)
popd
endlocal
exit/B
Remove echo from copy and del lines above, if it seems to do the work.
I have a txt file with values like this:
3.6.4.2
3.6.5.1
3.6.5.10
3.6.5.11
3.6.5.12
3.6.5.13
3.6.5.2
3.6.7.1
3.6.7.10
3.6.7.11
3.6.7.2
3.6.7.3
I need to write a batch script and return a sorted output. The problem is with last column, numbers .10 and .11 should go after .3 and so. I need the "latest version" to be on the bottom, which in this case is 3.6.7.11
In Linux I used "sort -t"." -k1n,1 -k2n,2 -k3n,3 -k4n,4" but I can't get it working with batch script.
Also I am not allowed to use Cygwin or PowerShell for some reasons.
In my batch code I am so far trying only various versions of this but nothing is working for me:
sort /+n versions.txt
The output used in this question is simply
sort versions.txt
It looks like that command sort is doing it correctly until I don't have 2 digits number used.
This is a common problem in Batch files. All sorting methods use a string comparison, where "10" comes before "2", so it is necessary to insert left zeros in the numbers less than 10. The Batch file below do that, but instead of generate a new file with the fixed numbers, it uses they to create an array that will be automatically sorted. After that, the array elements are shown in its natural (sorted) order.
EDIT: I modified the code in order to manage two digits numbers in the four parts.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=." %%a in (input.txt) do (
rem Patch the four numbers as a two digits ones
set /A "a=100+%%a, b=100+%%b, c=100+%%c, d=100+%%d"
rem Store line in the proper array element
set "line[!a:~1!!b:~1!!c:~1!!d:~1!]=%%a.%%b.%%c.%%d"
)
rem Show array elements
for /F "tokens=2 delims==" %%a in ('set line[') do echo %%a
Output:
3.6.4.2
3.6.5.1
3.6.5.2
3.6.5.10
3.6.5.11
3.6.5.12
3.6.5.13
3.6.7.1
3.6.7.2
3.6.7.3
3.6.7.10
3.6.7.11
Based on your example this will work. If you should somehow end up with examples like 3.6.5.02 and 3.6.5.2, then this code will not work.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-4 delims=. " %%G in (FILE.TXT) do (
set N=0%%J
set SORT[%%G%%H%%I!N:~-2!]=%%G.%%H.%%I.%%J
)
for /F "tokens=2 delims==" %%N in ('set SORT[') do echo %%N
pause
Here is my solution working with 2 temporary files which works also if one of the other 3 version numbers becomes ever greater than 9.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "VersionsFile=versions.txt"
rem Delete all temporary files perhaps existing from a previous
rem run if user of batch file has broken last batch processing.
if exist "%TEMP%\%~n0_?.tmp" del "%TEMP%\%~n0_?.tmp"
rem Insert a leading 0 before each number in version string if the
rem number is smaller than 10. And insert additionally a period at
rem start of each line. The new lines are written to a temporary file.
for /F "useback tokens=1-4 delims=." %%A in ("%VersionsFile%") do (
if %%A LSS 10 ( set "Line=.0%%A." ) else ( set "Line=.%%A." )
if %%B LSS 10 ( set "Line=!Line!0%%B." ) else ( set "Line=!Line!%%B." )
if %%C LSS 10 ( set "Line=!Line!0%%C." ) else ( set "Line=!Line!%%C." )
if %%D LSS 10 ( set "Line=!Line!0%%D" ) else ( set "Line=!Line!%%D" )
echo !Line!>>"%TEMP%\%~n0_1.tmp"
)
rem Sort this temporary file with output written to one more temporary file.
rem The output could be also printed to __stdout__ and directly processed.
%SystemRoot%\System32\sort.exe "%TEMP%\%~n0_1.tmp" /O "%TEMP%\%~n0_2.tmp"
rem Delete the versions file before creating new with sorted lines.
del "%VersionsFile%"
rem Read sorted lines, remove all leading zeros after a period and also
rem the period inserted at start of each line making it easier to remove
rem all leading zeros. The lines are written back to the versions file.
for /F "useback delims=" %%L in ("%TEMP%\%~n0_2.tmp") do (
set "Line=%%L"
set "Line=!Line:.0=.!"
set "Line=!Line:~1!"
echo !Line!>>"%VersionsFile%"
)
rem Finally delete the two temporary files used by this batch file.
del "%TEMP%\%~n0_?.tmp" >nul
endlocal
The first temporary file with unsorted lines contains for input example:
.03.06.04.02
.03.06.05.01
.03.06.05.10
.03.06.05.11
.03.06.05.12
.03.06.05.13
.03.06.05.02
.03.06.07.01
.03.06.07.10
.03.06.07.11
.03.06.07.02
.03.06.07.03
The second temporary file with the sorted lines contains for input example:
.03.06.04.02
.03.06.05.01
.03.06.05.02
.03.06.05.10
.03.06.05.11
.03.06.05.12
.03.06.05.13
.03.06.07.01
.03.06.07.02
.03.06.07.03
.03.06.07.10
.03.06.07.11
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains %~n0 (name of batch file without path and file extension)
del /?
echo /?
endlocal /?
for /?
if /?
rem /?
set /?
setlocal /?
sort /?
Easiest solution would be to invoke PowerShell and treat the version numbers as actual System.Version objects. That way the Major, Minor, Build, and Revision segments will be treated as integers and sorted accordingly. You can call this from a batch script:
powershell "(gc textfile.txt | %%{[version]$_} | sort) -split ' '"
That's it. Easy one-liner. If doing it from the cmd prompt, replace the double %% with a single %. Here's a breakdown of the command:
Get the following as a string:
Get the contents of textfile.txt
For each line, cast the data as a System.Version object.
Sort as versions
The string will be a single line separated by spaces. Split on the spaces.
Output is as follows:
3.6.4.2
3.6.5.1
3.6.5.2
3.6.5.10
3.6.5.11
3.6.5.12
3.6.5.13
3.6.7.1
3.6.7.2
3.6.7.3
3.6.7.10
3.6.7.11
Partial credit should go to this question and accepted answer.
In pure batch scripting, you could use the following code snippet:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
> "versions.tmp" (
for /F "usebackq tokens=1,2,3,4 delims=." %%I in ("versions.txt") do (
set "ITEM1=000%%I" & set "ITEM2=000%%J" & set "ITEM3=000%%K" & set "ITEM4=000%%L"
echo !ITEM1:~-4!.!ITEM2:~-4!.!ITEM3:~-4!.!ITEM4:~-4!^|%%I.%%J.%%K.%%L
)
)
< "versions.tmp" (
for /F "tokens=2 delims=|" %%S in ('sort') do (
echo %%S
)
)
del /Q "versions.tmp"
endlocal
exit /B
This creates a temporary file, which contains the original line, prefixed with padded version numbers and a separtor |. Padded numbers means that each component is padded with leading zeros to four digits. Here is an example based on youe sample data:
0003.0006.0004.0002|3.6.4.2
0003.0006.0005.0001|3.6.5.1
0003.0006.0005.0010|3.6.5.10
0003.0006.0005.0011|3.6.5.11
0003.0006.0005.0012|3.6.5.12
0003.0006.0005.0013|3.6.5.13
0003.0006.0005.0002|3.6.5.2
0003.0006.0007.0001|3.6.7.1
0003.0006.0007.0010|3.6.7.10
0003.0006.0007.0011|3.6.7.11
0003.0006.0007.0002|3.6.7.2
0003.0006.0007.0003|3.6.7.3
This temporary file is then passed over to sort which does a purely alphabetic sorting. Since the numbers are padded, the sort order equals the true alphanumeric order. Here is the sorting result using the above example:
0003.0006.0004.0002|3.6.4.2
0003.0006.0005.0001|3.6.5.1
0003.0006.0005.0002|3.6.5.2
0003.0006.0005.0010|3.6.5.10
0003.0006.0005.0011|3.6.5.11
0003.0006.0005.0012|3.6.5.12
0003.0006.0005.0013|3.6.5.13
0003.0006.0007.0001|3.6.7.1
0003.0006.0007.0002|3.6.7.2
0003.0006.0007.0003|3.6.7.3
0003.0006.0007.0010|3.6.7.10
0003.0006.0007.0011|3.6.7.11
Finally, if you want to return the latest version number only, echo %%S by set "LVER=%%S" and place echo !LVER! after the closing ) of the second for /F loop.
Update:
Here is a solution that does not produce any temporary files, but uses a pipe | instead. Since a pipe creates new cmd instances for both left and right sides, and due to the fact that the (console) outputs are built in tiny bits and that there are multiple arithmetic operations done, it is rather slow:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
(
for /F "usebackq tokens=1,2,3,4 delims=." %%I in ("versions.txt") do #(
set /A "10000+%%I" & echo( ^| set /P "=."
set /A "10000+%%J" & echo( ^| set /P "=."
set /A "10000+%%K" & echo( ^| set /P "=."
set /A "10000+%%L" & echo(
)
) | (
for /F "tokens=1,2,3,4 delims=." %%S in ('sort') do #(
set /A "%%S-10000" & echo( ^| set /P "=."
set /A "%%T-10000" & echo( ^| set /P "=."
set /A "%%U-10000" & echo( ^| set /P "=."
set /A "%%V-10000" & echo(
)
)
endlocal
exit /B
Left Side of the Pipe:
Instead of the substring expansion syntax like in the above approach using a temporary file, I add 10000 to every component of the version numbers (similar to Aacini's answer) in order to avoid delayed expansion, because this is not enabled in either new cmd instance. To output the resulting values, I make use of the fact that either of the for /F loops are running in cmd context rather than in batch context, where set /A outputs the result to STDOUT. set /A does not terminate its output with a line-break, so I use set /P to append a . after each but the last item, which in turn does not append a line-break. For the last item I append a line-break using a blank echo.
Right Side of the Pipe:
The sorting is again accomplished by the sort command, whose output is parsed by for /F. Here the previously added value 10000 is subtracted from each component to retrieve the original numbers. For outputting the result to the console, the same technique is used as for the other side of the pipe.
Piped Data:
The data passed over by the pipe looks like this (relying on the example of the question once again):
10003.10006.10004.10002
10003.10006.10005.10001
10003.10006.10005.10010
10003.10006.10005.10011
10003.10006.10005.10012
10003.10006.10005.10013
10003.10006.10005.10002
10003.10006.10007.10001
10003.10006.10007.10010
10003.10006.10007.10011
10003.10006.10007.10002
10003.10006.10007.10003
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.
This question already has an answer here:
add multiple lines in middle of txtfile
(1 answer)
Closed 8 years ago.
ok here a file txt.txt
line1
line2
add after this line blah blah blah
line4
etc
etc
i want to add a lines after add after this line but i dont know what comes in the same line after add after this line
what i want to know is what all changes i have to make in [this] code
#ECHO OFF
SETLOCAL
SET /p info="enter info : "
:: read addafter line
(
FOR /f "delims=" %%i IN (poison1.txt) DO (
SET addafter=%%i
FOR /f "delims=" %%n IN (' findstr /n "^" txtfile.txt') DO (
SET line=%%n
SETLOCAL ENABLEDELAYEDEXPANSION
SET line=!line:*:=!
ECHO(!line!
IF "!line!"=="!addafter!" TYPE your_multiline_addition.txt
ENDLOCAL
)
)
)>newfile.txt
FC newfile.txt txtfile.txt
GOTO :eof
any help is much appreciated.
dont know how to use wild cards in string manipulation
You know what your line contains - it's in !line!
You know what you want to find - it's in !addafter!
So - to find whether !addafter! is in !line! all you need to do is
echo !line!|findstr "!addafter!" >nul
if errorlevel 1 (echo not found) else (echo found)
So read the documentation shown by
findstr /?
and work out what you need to find "beginining" or "ending" with ... , whether or not you need case-insensitivity and whether you need to use you search-for-me-string as a literal or a "regular expression" (noting that by default REGULAR EXPRESSION is assumed) and whether or not you want to *C*ombine "a string with spaces" or search for "any word in a set" without *C*ombining them together as a single string.