How can i get the value to myVar attribute to use in the other part of code.
set myVar =
ECHO "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml" | find /i "2014-04-12" | (set /p myVar= & set myVar)
ECHO myvar "%myVar%" /**** This is empty String**********/
Maybe what you are asking is:
This line
echo "someValue" | find /i "Value" | (set /p myVar= & set myVar)
will show you the variable has been set. echo command send its output to find command, and the output of it is piped into the set /p, assigning the value to the variable, value that the set will show in console.
Then the following line
echo myvar "%myVar%"
shows no data. Why?
When cmd finds a pipe definition, with data flowing from one program to other, each of the elements in the pipe is a separate process with its own copy of the environment block of the parent process (where the batch file is running), so the set /p myVar= & set myVar is not running inside the same instance as the batch file. The variable is set in another process, and as the changes are made in a copy of the environment, they are not visible from the parent process.
Try like this :
#echo off
for /f "delims=" %%a in ('ECHO "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml" ^| find /i "2014-04-12"') do set MyVar=%%a
echo %MyVar%
I don't anderstand what you are trying to do.
But assuming that you're looking for a .xml file who contain the string "2014-04-12" in a directory. The code will be :
#echo off
for /f "delims=" %%a in ('dir /b/a-d *.xml ^| find /i "2014-04-12"') do set MyVar=%%a
Annotating your code:
1: set myVar =
2: ECHO "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml" | find /i "2014-04-12" | (set /p myVar= & set myVar)
3: ECHO myvar "%myVar%" /**** This is empty String**********/
On line 1:, there is a space between myVar and =. This is incorrect. CMD.EXE considers that there is a variable called "myVar ". Change this to "myVar".
On line 2:, I guess that you intended to use TYPE to output the file "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml". In any case you could have written this, instead:
FIND /I "2014-04-12" httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml
Related
I'm writing a simple batch file to create new, user-defined files.
My question is how to accept input for the extension with or without a period, and not yield a double-period (ie. name1..txt).
I also want to avoid having to print instructions for them to include/not include it. Thanks for helping!
My approach is below. I want to look for a period at the beginning of the "extension" variable, ext, and run the appropriate FOR loop to create files.
setlocal enabledelayedexpansion
set num=
set name=
set ext=
:setnum
set /P "num=Number of files to create?: "
If not defined num Echo.&Echo You must enter a number to continue...&goto:setnum
:setname
set /P "name=Enter "root" name of file:"
If not defined name echo.&Echo You must enter the name of your new files to continue...&goto:setname
:setext
set /P "ext=What will be the file extension?:"
If not defined ext echo.&Echo You must enter an extension to continue...&goto:setext
pause
%ext:~0,1% | FINDSTR /R "[.]" && pause & goto:extNoDot
%ext:~0,1% | FINDSTR /R "[.]" || pause & goto:extYesDot
:extNoDot
for /L %%a in (1,1,%num%) do (echo %%a >> !name!%%a%ext%)
goto:eof
:extYesdot
for /L %%a in (1,1,%num%) do (echo %%a >> !name!%%a.%ext%)
goto:eof
:eof
EXIT /b
You don't actually state what's wrong with your current code. Without testing it, I can see that these two lines must be giving you problems:
%ext:~0,1% | FINDSTR /R "[.]" && pause & goto:extNoDot
%ext:~0,1% | FINDSTR /R "[.]" || pause & goto:extYesDot
This is because you've got %ext:~0,1% at the beginning of a line as if it were a command. What you seem to be trying to do is pipe these to the FINDSTR command. Therefore you need to echo them:
echo %ext:~0,1% | FINDSTR /R "[.]" && pause & goto:extNoDot
echo %ext:~0,1% | FINDSTR /R "[.]" || pause & goto:extYesDot
However, using an external command here is overkill. You should do the following instead:
rem Remove any optional dot at the start
if "%ext:~0,1%" == "." set "ext=%ext:~1%"
And then just carry on as if ext never had a dot in the first place (no need for separate nodot and yesdot labels).
In a Windows batch file I needed to retrieve the result of a program execution into a variable, when the program call was complicated with spaces and several options. After much discussion, a workaround was found by using CALL:
FOR /F "delims=" %%G IN ('CALL "C:\path with spaces\foo.bat" "blah blah='foobar' blah"') do set foo=%%G
Please see the following question for more details and to understand the context:
Retrieve command output to variable when command has spaces
In reality the batch file calls PostgreSQL 9.3, like this:
SET PSQL_EXE=C:\Program Files\Foo\Bar\PostgreSQL\9.3\bin\psql.exe
SET FOO=1
REM psql query should result in a 0 or 1 based on the mydbproperty table value
FOR /F %%G IN ('call "%PSQL_EXE%" -U pguser -d MyDB -p %PG_PORT% -c "select string_value from mydb.uri as uri, mydb.property as prop where uri.id = prop.property_uriid and uri.namespace_uri='http://example.com/foo/bar/' and uri.simplename = 'fooBar'" -q -t -w') DO SET FOO=%%G
REM next line doesn't have ERRORLEVEL set
IF !ERRORLEVEL! NEQ 0 EXIT !ERRORLEVEL!
Unfortunately it appears that this format results in a separate cmd instance, so any error that occurred in calling pgsql (e.g. the lack of a password file) does not get passed back and the %ERRORLEVEL% does not get set. Specifically pgsql prints out:
psql: fe_sendauth: no password supplied
Yet %ERRORLEVEL% (and !ERRORLEVEL!) is still 0.
See the following question for more info:
PSQL Error Level in Batch For Loop
So now the question is how to find out the %ERRORLEVEL%, now that I've succeeding in getting the response of psql. I'd prefer not to write to some temporary file---I want to do everything in memory.
(Note that, yes, the value I'm trying to query from the database and store in FOO will be either a 0 or a 1; not that it matters, but it does make things more confusing.)
I tried to test Aacini's proposed solution by simply seeing what would be returned in %%G:
FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "%PSQL_EXE%" -U user -d MyDB -p %PG_PORT% -c "select string_value from mydb.uri as uri, mydb.database_property as prop where uri.id = prop.property_uriid and uri.namespace_uri='http://example.com/foo/bar/' and uri.simplename = 'fooBar'" -q -t -w ^& ECHO !ERRORLEVEL!"') DO (
ECHO %%G
)
Because psql could not find a password file, this prints an error to stderr (as expected) and the ECHO outputs 0---so the ERRORLEVEL is not getting sent back to the DO clause. But if I split out the command from the FOR loop and just run it directly, it shows the ERRORLEVEL (2) just fine!
"%PSQL_EXE%" -U user -d MyDB -p %PG_PORT% -c "select string_value from mydb.uri as uri, mydb.database_property as prop where uri.id = prop.property_uriid and uri.namespace_uri='http://example.com/foo/bar/' and uri.simplename = 'fooBar'" -q -t -w
ECHO !ERRORLEVEL!
Update: After being advised that I need to escape the escapes if I have delayed expansion already turned on, I took the given answer and updated it to take into account that the program I'm calling won't return a value if it generates an error. So here's what I do; first I make sure two variables are undefined:
SET "var1="
SET "var2="
Then I use the loop that #Aacini and #jeb gave, but inside the loop I do this:
if NOT DEFINED var1 (
SET "var1=%%G"
) else (
SET "var2=%%G"
)
Then outside the loop after it has finished:
if DEFINED var2 (
ECHO received value: !var1!
ECHO ERRORLEVEL: !var2!
) ELSE (
ECHO ERRORLEVEL: !var1!
)
That seems to work. (Unbelievably---what gymnastics!)
A couple tricks must be combined in order to do that. The Batch file placed in the for /F set is executed in a new cmd.exe context, so we need a method to report its errorlevel when such command ends. The only way to do that is by first executing the desired Batch file via a cmd.exe /C placed explicitly, and after it insert a command that report its errorlevel. However, the commands placed in the for /F set are executed in the command-line context, not in the Batch file context, so it is necessary to enable delayed expansion (/V:ON switch) in the explicit cmd.exe. After that, a simple echo !errorlevel! show the errorlevel returned by the Batch file.
#echo off
setlocal
set "foo="
FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "path with spaces\foo.bat" "blah blah='foobar' blah" ^& echo !errorlevel!"') do (
if not defined foo (
set "foo=%%G"
) else (
set "errlevel=%%G"
)
)
echo String output from foo.bat: "%foo%"
echo Errorlevel returned by foo.bat: %errlevel%
This is "path with spaces\foo.bat":
#echo off
echo String from foo.bat
exit /B 12345
And this is the output of previous code:
String output from foo.bat: "String from foo.bat"
Errorlevel returned by foo.bat: 12345
If delayed expansion is already enabled before in the main batch file then a quite different syntax have to be used.
^!ERRORLEVEL^! This is necessary to avoid, that the !ERRORLEVEL! is evaluated in the context of the batch file, before the FOR line will be executed.
^^^& is necessary to bring a single ^& to the inner cmd /V:ON expression
#echo off
setlocal EnableDelayedExpansion
set "foo="
REM *** More carets must be used in the next line ****
FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "path with spaces\foo.bat" "blah blah='foobar' blah" ^^^& echo ^!errorlevel^!"') do (
if not defined foo (
set "foo=%%G"
) else (
set "errlevel=%%G"
)
)
EDIT: Reply to OP's comments
My first solution assumes that the program always output one line, so it took the errorlevel from the second output line. However, the new specification indicate that when the program ends because an error, the first output line is not sent. It is very easy to fix this detail, and the corrected code is below.
#echo off
setlocal EnableDelayedExpansion
set "foo="
FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "path with spaces\foo.bat" "blah blah='foobar' blah" ^^^& echo errlevel=^!errorlevel^!"') do (
set "str=%%G"
if "!str:~0,8!" equ "errlevel" (
set "errlevel=!str:~9!"
) else (
set "foo=%%G"
)
)
echo String output from foo.bat: "%foo%"
echo Errorlevel returned by foo.bat: %errlevel%
However, the original specification indicate that the executed program is a BATCH file called "C:\path with spaces\foo.bat". This code correctly works when the executed program is a Batch .BAT file as requested, but it does not work if the executed program is an .exe file, as I clearly indicated in my first comment: "Note that C:\path with spaces\foo.bat must be a .bat file! This method don't works if such file is an .exe". This way, you just need to create the "foo.bat" file with the execution of the .exe and an exit /B %errorlevel% command in the next line. Of course, if you directly test this method by directly executing the .exe program, it never will work...
2ND EDIT: Some explanations added
The purpose of this solution is that giving a Batch file "C:\path with spaces\foo.bat" that show some output and return an errorlevel value, like this one:
#echo off
echo String from foo.bat
exit /B 12345
... it may be called by another Batch file that get both the output and the errorlevel. This can be done in a very easy way using an auxiliary file:
#echo off
call "C:\path with spaces\foo.bat" > foo.txt
set errlevel=%errorlevel%
set /P "foo=" < foo.txt
echo String output from foo.bat: "%foo%"
echo Errorlevel returned by foo.bat: %errlevel%
However, the request indicate: "not to write to some temporary file---I want to do everything in memory".
The way to get the output from another command is via FOR /F, like this:
FOR /F "delims=" %%G in ('CALL "C:\path with spaces\foo.bat" "blah blah='foobar' blah"') do set foo=%%G
However, The Batch file placed in the FOR /F set is executed in a new cmd.exe context. To be more precise, the execution of foo.bat in previous FOR command is entirely equivalent to the next code:
CMD /C CALL "C:\path with spaces\foo.bat" "blah blah='foobar' blah" > tempFile & TYPE tempFile & DEL tempFile
Each line in tempFile is assigned to the %%G replaceable parameter and the set foo=%%G command is executed. Note that previous line is executed as if it was entered at the command-prompt (command-line context), so several commands does not work in this context (like goto/call to a label, setlocal/endlocal, etc) and delayed expansion is set to the initial value of cmd.exe, usually disabled.
In order to simplify the following description, we use a shorter, but similar FOR /F command that is shown with the equivalent internal code of the execution of foo.bat below it:
FOR /F %%G in ('CALL "foo.bat"') do set foo=%%G
-> CMD /C CALL "foo.bat" <- we also omit the "tempFile" parts
This way, we need a method to report the errorlevel of foo.bat when such command ends. The only way to do that is by first executing the desired Batch file via a cmd.exe /C placed explicitly, and after it insert a command that report its errorlevel. That is:
FOR /F %%G IN ('"CMD /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G
-> CMD /C "CMD /C CALL foo.bat ^& echo !errorlevel!"
Note that the ampersand sign must be escaped this way: ^&; otherwise it will incorrectly split the commands placed in the set of the FOR /F command. In previous line both the foo.bat and the echo !errorlevel! command are executed via the nested CMD /C.
However, the commands placed in the FOR /F set are executed in the command-line context as explained before, so it is necessary to enable delayed expansion (/V:ON switch) in the explicit cmd.exe; otherwise, the echo !errorlevel! just not works:
FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G
-> CMD /C "CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"
Ok. Note that the previous description assumed that delayed expansion was DISABLED when the FOR /F command is executed. What are the changes if it was enabled? 1. The !errorlevel! is replaced by the current errorlevel value, and 2. Any caret that is used to escape a character is removed. These changes are done when the line is parsed, before the FOR /F command is executed. Lets see this point with detail:
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G
-> After parsed:
FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat & echo 0"') do set foo=%%G
Previous line issue an error because the unescaped &, as usual. We need to preserve both the exclamation marks and the caret of the ampersand. Preserve exclamation marks is easy: ^!errorlevel^!, but how we preserve the caret in ^&? If we use ^^& the first caret preserve the second one, but the next character to parse is & alone and the usual error appears! So, the right way is: ^^^&; the first caret preserve the second one and give ^, and the ^& preserve the ampersand:
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^^^& echo ^!errorlevel^!"') do set foo=%%G
-> After parsed:
FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G
I am trying to convert a long filename to a short filename (8.3) on Windows.
A batch-file with a command line argument works as intended:
short.bat:
#echo OFF
echo %~s1
calling short.bat C:\Documents and Settings\User\NTUSER.DAT returns C:\DOCUM~1\USER\NTUSER.DAT
However, I don't like having an extra .bat-file for this. I would rather call cmd.exe with the whole command from a ruby script.
How can I do this?
As an intermediate step I tried to hardcode the path in the batch-file, but that does not work:
short1.bat:
#echo OFF
SET filename="C:\Documents and Settings\User\NTUSER.DAT"
echo %filename%
echo %~sfilename%
echo %filename% works, but echo %~sfilename% gives the following error:
The following usage of the path operator in batch-parameter
substitution is invalid: %~sfilename%
For valid formats type CALL /? or FOR /?
If short1.bat works, how can I convert this into a one-liner that can be called with cmd.exe \c ...?
There is another question (how to get DOS path instead of Windows path), however that one is specifically asking for the path of the current directory.
cmd /c for %A in ("C:\Documents and Settings\User\NTUSER.DAT") do #echo %~sA
Replace the filename.txt to the filename you want to convert to 8.3
dir /x filename.txt
You will then have to split the result with whitespace as your delimiter (\s in regex).
Then the value with the ~ is your short filename. If your filename is short to begin with, then you won't find a string containing a ~.
Here is an example that read in the registry the location of your "appdata\local" folder and convert it to short path:
cls
#echo off
cd /d "%~dp0"
chcp 65001 >nul
for /f "skip=1" %%a in ('"wmic useraccount where name='%USERNAME%' get sid"') do (
for %%b in (%%a) do set current_SID=%%b
)
set current_username=%USERNAME%
set current_userprofile=%USERPROFILE%
set key_to_read=HKEY_USERS\%current_SID%\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
set value_to_read=Local AppData
rem If value_to_read contains ? space(s) set tokens to 2+?
for /f "usebackq eol= tokens=3,* delims= " %%a in (`reg query "%key_to_read%" /v "%value_to_read%" 2^>nul ^| find "%value_to_read%"`) do (
set value_type=%%a
set data_read=%%b
)
set data_read=%data_read:USERPROFILE=current_userprofile%
call set "data_read=%data_read%"
set current_local_appdata=%data_read%
set current_local_appdata_temp=%current_local_appdata%\Temp
echo %current_local_appdata_temp%
for %%a in ("%current_local_appdata_temp%") do set "current_local_appdata_temp_short=%%~sa"
echo %current_local_appdata_temp_short%
pause
exit
I'm tryng to make a .bat file to add blank lines to a text file, based on the amount of lines that match a condition. This is what a have:
#echo
SET /a maxLineas = 50
SET cantLineasDetalle="type texto.txt | find /i /c "D01" "
SET /a cantLineasAgregar = %maxLineas% - %cantLineasDetalle%
:loop
echo. >> texto.txt
set /a cantLineasAgregar-=1
if %cantLineasAgregar% GTR 0 goto loop
The trouble is that var "cantLineasDetalle" isn't storing the value that I want it to do.
How do I asign the result of the execution of 'type texto.txt | find /i /c "D01"' to a variable?
Thanks in advance,
Esteban.
As the previous answer indicated, you use a FOR /F loop to store the results of a command into a variable.
find "search" <file can be significantly more efficient than type file | find "search" if the file is large.
When executed within a FOR /F IN() clause, all special characters must be either quoted or escaped. In your case the pipe will need to be escaped, or if you take my suggestion, the < will need to be escaped.
echo. >>file will append a line with a space to the file. Also, it is safer to use echo( instead of echo., but you will probably never run into a problem with echo. To get a blank line without a space use echo(>>file
When using SET /A to do math, you can refer to variables directly without enclosing them in percents. It also works with the percents.
Lastly, it is much more efficient to append the lines within a FOR /L loop instead of using a GOTO loop.
#echo off
set /a maxLineas=50
for /f %%N in('find /i /c:"D01" ^<texto.txt') do set /a cantLineasDetalle=%%N
set /a cantLineasAgregar=maxLineas-cantLineasDetalle
for /l %%N in (1 1 %cantLineasAgregar%) do echo(>>texto.txt
The entire script could be compressed to the following (maxLineas is now 0 based)
#echo off
set /a maxLineas=50-1
for /f %%N in('find /i /c:"D01" ^<texto.txt') do for /l %%I in (%%N 1 %maxLineas%) do echo(>>texto.txt
for /f %%i in ('type texto.txt | find /i /c "D01"') do SET cantLineasDetalle = %%i
Is it possible to set the output of TYPE or ECHO as a variable in a batch file?
Convoluted, and it only works for a single line, but general:
for /f "delims=" %%x in ('some command with output') do set "Var=%%x"
For echo you don't need to do anything special, just change
echo Foo
into
set Var=Foo
And for files there is also the option of either
set /p Var=<file.txt
or
for /f "delims=" %%x in (file.txt) do set "Var=%%x"