apparently this batch file should return the concatenation of the input files given as arguments, but it is not working:
set files=
for %%i in (%1 %2 %3 %4 %5 %6 %7 %8) do (
echo %%i
set files=%files% %%i
)
echo "the file list is %files%"
when invoked with:
mybatchfile.bat example1.txt example2.txt
the expected result should be:
example1.txt
example2.txt
the file list is example1.txt example2.txt
but in the final line only has "example2.txt". Any idea???
You need to enable delayed variable expansion (note the expansion !files! in the following code):
set files=
setlocal EnableDelayedExpansion
for %%i in (%1 %2 %3 %4 %5 %6 %7 %8) do (
echo %%i
set files=!files! %%i
)
endlocal & set files=%files%
echo "the file list is %files%"
Related
I have a phrase that needs to be outputted in results.txt. The phrase comes from x.txt. For example: "I have two kids", it should output "kids two have I" in results.txt.
UPDATE
Its already working but i want no loop.
Pls see code below
Code
#ECHO OFF
set /p content=<x.txt
SET var=!content: =,!
SET rev=
:LOOP
IF NOT "!var!"=="" (
FOR /F "delims=, tokens=1,*" %%F IN ("!var!") DO (
SET rev=%%F,!rev!
SET var=%%G
)
) ELSE (
SET rev=!rev:~0,-1!
GOTO ENDLOOP
)
GOTO LOOP
:ENDLOOP
ECHO !rev:,= ! > results.txt
This takes the first four words (of each line) of the text file and rewrites them in reverse order to result.txt:
>result.txt (for /f "tokens=1-4" %%a in (x.txt) do echo %%d %%c %%b %%a)
Another solution (for a one-line text file, unspecified number of words):
#ECHO OFF
setlocal enabledelayedexpansion
echo I have two little but wonderful kids>x.txt
<x.txt set /p x=
for %%a in (%x%) do set "res=%%a !res!"
>result.txt echo %res:~0,-1%
(although technically, the for command is a loop on its own)
Without any form of a loop, if you can live with some spaces at the front:
#ECHO OFF
setlocal
echo I have two kids>x.txt
<x.txt set /p x=
call :reverse %x%
goto :eof
:reverse
set "rev=%9 %8 %7 %6 %5 %4 %3 %2 %1"
echo Reverse without any form of loop: "%rev%"
for /f "tokens=*" %%a in ("%rev%") do echo To get rid of the spaces, you need a FOR loop: "%%a"
This is limited to a maximum of nine words because cmd supports only %1to %9
You can use more parameters (words) by using the shift command, but that would mean using a loop.
Here are three different ways.
Replacement magic (borrowed from Aacini)
For tokens
Recursion
#echo off
setlocal EnableDelayedExpansion
set "var=One two three four five"
call :rev1
call :rev2
call :rev3
exit /b
:rev1 -- replacement magic
setlocal
set "rev="
set rev=%var: = !rev!&Set rev=% !rev!
echo %0 !rev!
exit /b
:rev2 -- FOR toekns
setlocal
FOR /F "tokens=1-5" %%1 in ("%var%") do set "rev=%%5 %%4 %%3 %%2 %%1"
echo %0 !rev!
exit /b
:rev3 -- recursion
setlocal
set "rev="
call :__rev3_rec %var%
echo %0 !rev!
exit /b
:__rev3_rec
if "%1" == "" exit /b
call :__rev3_rec %2 %3 %4 %5
set "rev=%rev%%1 "
exit /b
I'm not sure if I've understood your question fully, so this is intended to reverse the order of each space separated substring per line.
For the task, I'd leverage PowerShell.
At the powershell prompt, (powershell.exe):
GC '.\x.txt'|%{$L=$_.Split(' ');[Array]::Reverse($L);$L -Join ' '}|SC '.\results.txt'
At the command-prompt, (cmd.exe):
"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoP "GC '.\x.txt'|%{$L=$_.Split(' ');[Array]::Reverse($L);$L -Join ' '}|SC '.\results.txt'
From a batch-file:
#"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoP "GC '.\x.txt'|%%{$L=$_.Split(' ');[Array]::Reverse($L);$L -Join ' '}|SC '.\results.txt'
If you're only working on the first line of the file, as opposed to all of them, a small modification is needed.
At the powershell prompt, (powershell.exe):
GC '.\x.txt' -To 1|%{$L=$_.Split(' ');[Array]::Reverse($L);$L -Join ' '}|SC '.\results.txt'
At the command-prompt, (cmd.exe):
"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoP "GC '.\x.txt' -To 1|%{$L=$_.Split(' ');[Array]::Reverse($L);$L -Join ' '}|SC '.\results.txt'
From a batch-file:
#"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoP "GC '.\x.txt' -To 1|%%{$L=$_.Split(' ');[Array]::Reverse($L);$L -Join ' '}|SC '.\results.txt'
Please note that, in both cases, if your strings are actually doublequoted, those will not move position. This means that "I have two kids", would output as kids" two have "I, not "kids two have I".
I'm trying to do this :
cd C:\Users\Public
%1 /s
Where %1 is the name of and executable that changes based on a where it's being executed, but is otherwise identical. The issue is that the /s is being seen as a command or program and not an argument to the executable passed in %1. Is what I'm trying to do possible? If so, what's the syntax?
If you save what you have as testit.bat and then when you go to run it add the parameter after your batch file name: "testit dir" it will run the dir command with the /s switch.
Next example could help. Note:
" double quotes using: If a single parameter contains spaces, you can still pass it as one item by surrounding in "quotes" - this works well for long filenames: Syntax : Escape Characters, Delimiters and Quotes;
%*in a batch script refers to all the arguments (e.g. %1 %2 %3 %4 %5 ... %255) but only arguments %1 to %9 may be referenced by number: Command Line arguments (Parameters)
The cliparsearg.bat script prints all command line arguments:
==>type cliparsearg.bat
#echo OFF >NUL
set /A "ii=0"
:loopfor
echo param %%%ii% = %0
SHIFT
set /A "ii+=1"
if not [%0]==[] goto :loopfor
echo all %%* = %*
goto :eof
==>cliparsearg.bat withoutspaces /S 3rdPar
param %0 = cliparsearg.bat
param %1 = withoutspaces
param %2 = /S
param %3 = 3rdPar
all %* = withoutspaces /S 3rdPar
==>cliparsearg.bat with 2 spaces /S 3rdPar
param %0 = cliparsearg.bat
param %1 = with
param %2 = 2
param %3 = spaces
param %4 = /S
param %5 = 3rdPar
all %* = with 2 spaces /S 3rdPar
==>cliparsearg.bat "with 2 spaces" /S 3rdPar
param %0 = cliparsearg.bat
param %1 = "with 2 spaces"
param %2 = /S
param %3 = 3rdPar
all %* = "with 2 spaces" /S 3rdPar
Resources (basic required reading for Windows batch scripters):
An A-Z Index of the Windows CMD command line
Windows CMD Shell Command Line Syntax
Thanks for all your help guys. I looked at the documentation and your comments and, though I'm still completely unsure of how this is actually supposed to work, I got it working sensibly. Here's the solution I came up with:
"%~1" /s
I have noticed what looks like a bug in Windows. I have a simple console application that returns -12. And a .cmd script that checks the return value using ERRORLEVEL and %ERRORLEVEL% NEQ 0. The script always works when the check is not nested inside an IF statement. When it is nested inside the IF statement, the return value detected by the script changes each time the script is ran. First time the script does not detect an error. The 2nd time, the script correctly detects error -12. On 3rd and subsequent runs, the script reports error 1.
Here is the script:
#echo off
IF "%1" NEQ "" (
ECHO RUNNING %1
%1
IF ERRORLEVEL 1 (
ECHO %1 returned positive value %ERRORLEVEL%
EXIT /B 1
)
IF %ERRORLEVEL% NEQ 0 (
ECHO %1 returned %ERRORLEVEL%
EXIT /B 1
)
)
You need setlocal enabledelayedexpansion.
#echo off
setlocal enableextensions enabledelayedexpansion
if "%1" NEQ "" (
echo running %1 %2 %3 %4 %5 %6 %7
%1 %2 %3 %4 %5 %6 %7
if errorlevel 1 (
echo %1 returned positive value !ERRORLEVEL!
exit /b 1
)
if !ERRORLEVEL! NEQ 0 (
echo %1 returned !ERRORLEVEL!
exit /b 1
)
)
endlocal
I've got bunch of text files with some content. First I wanted to number the lines globally. Then I extracted all lines that are duplicated somewhere (occur in any of given files at least twice). But now I need to mark all of these lines with the filename and line number of the first occurrence of this line. And now the funny part - it needs to be a windows batch file, using native windows tools. That's why I've got this problem to begin with.
So, to sum it up:
I have a file A with unique strings/lines, each of them is said to occur at least twice in given set of files.
I need to search these files and mark all occurrences of given line from A file with
-file name in which the line first occured
-line number in this file
This is my code with effort to number lines and format files.
#echo off
setlocal EnableDelayedExpansion
set /a lnum=0
if not [%1]==[] pushd %1
for /r %%F in (*.txt) do call :sub "%%F"
echo Total lines in %Files% files: %Total%
popd
exit /b 0
:Sub
set /a Cnt=0
for /f %%n in ('type %1') do (
set /a Cnt+=1
set /a lnum=!lnum!+1
echo ^<!lnum!^> %%n >> %1_ln.txt && echo ^<!lnum!^> >> %1_ln.txt && echo. >> %1_ln.txt
)
set /a Total+=Cnt
set /a Files+=1
echo %1: %Cnt% lines
#echo off
setlocal EnableDelayedExpansion
set lnum=0
if not "%~1" == "" pushd %1
rem "I've got bunch of text files..." (%%F is file name)
for /r %%F in (*.txt) do call :sub "%%F"
echo Total lines in %Files% files: %lnum%
popd
exit /b 0
:Sub "filename"
set Cnt=0
rem "... with some content." (%%n is line contents)
(for /f "usebackq delims=" %%n in (%1) do (
set /a Cnt+=1
rem "First I wanted to number the lines globally."
set /a lnum+=1
echo ^<!lnum!^> %%n
rem "Then I extracted all lines that are duplicated somewhere" (that were defined before)
if defined line[%%n] (
rem "I need to mark all of these lines with the filename and line number of the first occurrence of this line."
echo ^<!line[%%n]!^>
echo/
) else (
REM (Store the first occurrence of this line with *local* line number and filename)
set line[%%n]=!Cnt!: %1
)
)) > "%~PN1_ln.txt"
set /A Files+=1
echo %1: %Cnt% lines
exit /B
The above Batch program ignore empty lines in the input files and fail if they contain special Batch characters, like ! & < > |; this limitation may be fixed if required.
#ECHO OFF
SETLOCAL
FOR /f "delims=" %%s IN (A) DO (
SET searching=Y
FOR /f "delims=" %%f IN (
'dir /s /b /a-d *.txt') DO IF DEFINED searching (
FOR /f "tokens=1delims=:" %%L IN (
'findstr /b /e /n /l /c:"%%s" ^<"%%f"') DO IF DEFINED searching (
ECHO Line %%L IN "%%f" FOUND "%%s"
SET "searching="
)
)
)
Here's the meat of a routine that should do what you appear to be looking for - and that's as clear as mud.
It looks through the "A" file for each string in turn, assigns the string to %%s and sets the flag searching
Then it looks through the file list, assigning filenames to %%f
Then it executes a findstr to find the /c:"%%s" complete string %%s (including any spaces) in /l or literal mode (ie. not using regular expressions) for a line that both /b and /e begins and ends with the target (ie exactly matches) and /n numbers those lines.
The output of findstr will be in the format linenumber:linecontents so if this line is examined by the FORwith the option "delims=:" then the partion up to the first : is assigned to to %%L
So - %%L contains the line#, %%f the filename, %%s the string
Clearing searching having detected this line by setting its value to [nothing] means it's not NOT DEFINED hence no further lines will be reported from the current file, and no further filenames will be examined.
Now if you want to get a listing of ALL of the occurrences of the target lines, all you need to do is to REM-out the SET "searching=" line. Searching will then never be reset, so each line in each file is reported.
If you want some other combination, please clarify.
I have absolutely no idea whatever what you mean by "marking" a line.
#ECHO OFF & setlocal
for /f "tokens=1*delims==" %%i in ('set "$" 2^>nul') do set "%%i="
for %%a in (*.txt) do (
for /f %%b in ('find /v /c "" ^<"%%a"') do echo(%%b lines in %%a.
set /a counter=0, files+=1
for /f "usebackqdelims=" %%b in ("%%~a") do (
set /a counter+=1, total+=1
set "line=%%b"
setlocal enabledelayedexpansion
if not defined $!line! set "$!line!=%%a=!counter!=!line!"
for /f "delims=" %%i in ('set "$" 2^>nul') do (if "!"=="" endlocal)& set "%%i"
)
)
echo(%total% lines in %files% files.
for /f "delims=" %%a in (a) do set "#%%a=%%a"
for /f "tokens=2,3*delims==:" %%i in ('set "$" 2^>nul') do (
if defined #%%k echo("%%k" found in %%i at line %%j.
)
Script can handle !&<>|%, but not =.
I have this single line CMD file TEST.CMD:
for %%f in (%1 %2 %3 %4 %5 %6 %7 %8) DO ECHO %%f
If I run this:
TEST this is a test
it correctly echos each parameter on a separate line, i.e.,
this
is
a
test
However if a parameter contains asterisk it skips it. E.g.,
TEST this is a* test
Results in:
this
is
test
How do I get the parameter with an asterisk to be treated like a normal token?
Thanks.
The simplest method that works for most parameters is to transfer the parameters to an "array" of variables, and then use FOR /L to loop through the array. This is best achieved with delayed expansion.
This technique can process an arbitrary number of parameters - it is not limited to 9.
#echo off
setlocal
:: Transfer parameters to an "array"
set arg.cnt=1
:getArgs
(set arg.%arg.cnt%=%1)
if defined arg.%arg.cnt% (
set /a arg.cnt+=1
shift /1
goto :getArgs
)
set /a arg.cnt-=1
:: Process the "array"
setlocal enableDelayedExpansion
for /l %%N in (1 1 %arg.cnt%) do echo arg %%N = !arg.%%N!
The only way I have found without knowing the parameters beforehand is to echo the parameters in the for loop
for /f %%f in ('"echo %1 && echo %2 && echo %3 && etc"') DO ECHO %%f
You can't print that, the asterisk is a dynamic operator that matches "1 or more characters" in some commands, like the FOR command, the only way is to use the /F parameter that gets the output of a command.
See what happens if you use this:
#Echo OFF
Pushd "C:\"
Call :sub a b c * d e
:sub
for %%f in (%1 %2 %3 %4 %5 %6 %7 %8) DO ECHO %%f
Pause&Exit
(The FOR prints all the files in current directory)
Then you need to do... :
#Echo OFF
Call :sub a b c* d e
:sub
FOR /F "tokens=*" %%a in ('Echo %*') DO (ECHO %%a)
Pause&Exit