"Echo is OFF" text always inserted into batch file when SET variable is written to a batch file IF the file is made by another batch program - variables

I am trying to create a batch file using another batch program using:
#echo {code here}>>batch-program.bat, but whenever I try to write code to write the contents of a SET variable to a text file, the batch program does not write the code into the other batch file, but instead writes "Echo is OFF."
Code is here:
#echo off
#echo #echo off>>apt.bat
#echo color 2A>>apt.bat
#echo echo example-batch>>apt.bat
#echo cd C:/Users/Default/apt/assets>>apt.bat
#echo mkdir cmdInput>>apt.bat
#echo cd C:/Users/Default/apt/assets/cmdInput>>apt.bat
#echo set /p cmdInput= cmd->>apt.bat
#echo %cmdInput%>>used-cmdInput.txt>>apt.bat
#echo pause>>apt.bat
This should have created a batch file named apt.bat, and written into the batch file:
#echo off
echo color 2A
echo example-batch
cd C:/Users/Default/apt/assets
mkdir cmdInput
cd C:/Users/Default/apt/assets/cmdInput
set /p cmdInput= cmd-
%cmdInput%>>used-cmdInput.txt
pause
but the 9th line (%cmdInput%>>used-cmdInput.txt)
is instead converted into the text line "Echo is OFF".
Have I done anything wrong, or is it just a really weird bug?
EDIT: I found another problem in the program, that because mkdir cmdInput is always run when apt.bat is run, so it displays a error message because of apt.bat trying to create the directory cmdInput though it already exists. apt/assets. So I have changed the code a bit, so that the directory cmdInput is created in the first "creation" batch file (the program that was used to create apt.bat). mkdir cmdInput has been removed from apt.bat.

You need to escape > with ^ but you need to escape % with %
#echo %%cmdInput%%^>^>used-cmdInput.txt>>apt.bat
unless you want to output the contents of cmdinput where you need
#echo %cmdInput%^>^>used-cmdInput.txt>>apt.bat
You can add 2>nul to the end of a md command to suppress the error message generated if the directory already exists.
You should use backslashes \ in directorynames, not forward slashes /. In winbat, the forward slash is often used for command switches. Sometimes forward slashes will work for directorynames, but backslashes always work.

You just have to "escape" the percent-signs with another % and other special chars like > with a caret) to prevent evaluation of your variable %cmdInput% (which probably is empty - therefore the Echo is off).
Also a single #echo off is sufficient. No need to add a # to every line.
#echo off
echo #echo off>>apt.bat
echo color 2A>>apt.bat
echo echo example-batch>>apt.bat
echo cd C:/Users/Default/apt/assets>>apt.bat
echo mkdir cmdInput>>apt.bat
echo cd C:/Users/Default/apt/assets/cmdInput>>apt.bat
echo set /p cmdInput= cmd->>apt.bat
echo %%cmdInput%%^>^>used-cmdInput.txt>>apt.bat
echo pause>>apt.bat
A more elegant way is to use only a single redirection (cmdhas to open the file for writing just once):
#echo off
(
echo #echo off
echo color 2A
echo echo example-batch
echo cd C:/Users/Default/apt/assets
echo mkdir cmdInput
echo cd C:/Users/Default/apt/assets/cmdInput
echo set /p cmdInput= cmd-
echo %%cmdInput%%^>^>used-cmdInput.txt
echo pause
)>apt.bat

Related

Running sqlcmd in batch file works but running the same batch file as a scheduled task works and does nothing

I have looked at many SO questions/answers and though some seem similar to my issue they do not seem to be. The answers given fix issues the questions were asking about but will not solve my issue.
I have a batch file...
#ECHO ON
ECHO Disabling the following... >> C:\App\Debug.log
ECHO - V1 >> C:\Apps\Debug.log
FOR /F "tokens=* USEBACKQ" %%F IN (`sqlcmd -j -S DOMAIN\SQLSERVER -U username -P password -d DBNAME -Q "UPDATE [DBNAME].[dbo].[table1] SET ColOne='V1_OFF' WHERE ColOne='V1'"`) DO (
Echo %%F >> C:\Apps\Debug.log
)
EXIT /B
When I run this file at the command prompt it works perfectly fine. When I run it as a scheduled task it show me the echos but nothing for the for loop as expected.
Yes I have made sure the username (using whoami) is the same for the scheduled task set up as the manual run that I do.
Yes I know the user running the script has rights to everything (file access as well as DB access) because it works fine running it from the command prompt.
Scheduled task is set to run wither user is logged on or not.
Any ideas what might be wrong or what I can try for debugging purposes?
Thanks!
sqlcmd is perhaps not enough. cmd.exe in environment of scheduled task may fail to find the executable using local PATHEXT and local PATH environment variables. The executable should be specified with full qualified file name, i.e. drive + path + name + extension. Then the batch file does not anymore depend on the environment variables PATH and PATHEXT because of all files are referenced with full qualified file name.
for executes the specified command line with starting in background one more command process with %ComSpec% /c and the specified command line appended. This means executed is following with Windows installed on drive C::
C:\Windows\System32\cmd.exe /c sqlcmd -j -S DOMAIN\SQLSERVER -U username -P password -d DBNAME -Q "UPDATE [DBNAME].[dbo].[table1] SET ColOne='V1_OFF' WHERE ColOne='V1'"
for captures everything written to handle STDOUT of started command process. The lines of captured output are processed line by line by for after started cmd.exe terminated itself. Error messages output by started cmd.exe or the commands/executables executed by Windows command processor in background to handle STDERR are redirected to handle STDERR of command process processing the batch file and printed to console. But there is no console window on running a batch file as scheduled task. So error messages cannot be seen in this case.
The for command line can be modified easily here to get also error messages written into the C:\Apps\Debug.log.
FOR /F "tokens=* USEBACKQ" %%F IN (`sqlcmd -j -S DOMAIN\SQLSERVER -U username -P password -d DBNAME -Q "UPDATE [DBNAME].[dbo].[table1] SET ColOne='V1_OFF' WHERE ColOne='V1' 2^>^&1"`) DO (
The Microsoft article Using command redirection operators explains 2>&1. The two operators > and & must be escaped with ^ to be interpreted as literal characters on Windows command processor parsing the for command line before executing finally for which executes next %ComSpec% /c with the specified command line on which 2^>^&1 is changed already to 2>&1.
Does the log file C:\App\Debug.log contain with this modification following two lines?
'sqlcmd' is not recognized as an internal or external command,
operable program or batch file.
Yes, then no executable with file name sqlcmd is found by started cmd.exe. The best solution is referencing this executable with full qualified file name. See also: What is the reason for "X is not recognized as an internal or external command, operable program or batch file"?
Otherwise sqlcmd outputs perhaps an error message which should be now also in the log file C:\App\Debug.log.
It would be also possible to use following command line to let background cmd.exe write the error messages into a separate error log file C:\App\Error.log:
FOR /F "tokens=* USEBACKQ" %%F IN (`sqlcmd -j -S DOMAIN\SQLSERVER -U username -P password -d DBNAME -Q "UPDATE [DBNAME].[dbo].[table1] SET ColOne='V1_OFF' WHERE ColOne='V1'" 2^>C:\App\Error.log`) DO (
"tokens=* usebackq" results in first deleting all leading horizontal tabs and normal spaces on non-empty lines by for, then checking if the remaining line starts with ; in which case the line is also ignored and finally assigning the captured line not starting with ; and with leading tabs/spaces removed to loop variable F for further processing.
Better would be using the options usebackq^ delims^=^ eol^= not enclosed in double quotes which requires escaping the two spaces and the two equal signs with caret character ^ to be interpreted as literal characters by cmd.exe on parsing the command line before executing for. The line splitting behavior is disabled completed with delims= because of the definition of an empty list of delimiters. And no line except an empty line is ignored anymore because of end of line character modified from default ; to no character.
Finally a space on an echo line left to redirection operator >> is also output by echo and for that reason written as trailing space into the log file. Therefore no space should be used left to > or >> on printing a line with echo redirected into a file. But care must be taken on omitting the space character left to the redirection operator. The word left to redirection operator should not be 1, 2, ..., 9 as this would result in redirecting the output to these numbered handles into the specified file instead of the character 1, 2, etc. So if unknown text should be written into a file, it is better to specify first the redirection operator > or >> and the full qualified file name and next the echo command with the text to output. See also: Why does ECHO command print some extra trailing space into the file?
The three command lines with echo would be for this batch file:
ECHO Disabling the following...>> C:\App\Debug.log
ECHO - V1>> C:\Apps\Debug.log
>>C:\Apps\Debug.log ECHO %%F
following... is safe for being correct written into the file as also V1. %%F could be just 1 or a string ending with a space and a single digit and so it is better to specify the redirection first on the last echo command line to get finally executed by cmd.exe the command line ECHO %%F 1>>C:\Apps\Debug.log.

How to execute SQL scripts in alphabetical order using a batch file

Is it possible to execute all SQL scripts in a folder in alphabetical order using a batch file ?
Currently I uses the following code but it executes the scripts in the order in which they are kept instead of executing it in alphabetical order
SET Database=<<DatabaseName>>
SET ScriptsPath=<<FolderPath>>
SET ServerName=<<ServerName>>
IF EXIST "%ScriptsPath%output_CCF.txt" del "%ScriptsPath%output_CCF.txt"
type NUL > "%ScriptsPath%output_CCF.txt"
FOR /R "%ScriptsPath%" %%G IN (*.sql *.up) DO (
sqlcmd -d %Database% -S %ServerName% -i "%%G" -o "%%G.txt"
echo .................................................
>> "%ScriptsPath%output_CCF.txt"
echo Executing: "%%G" >> "%ScriptsPath%output_CCF.txt"
echo ...................................>> "%ScriptsPath%output_CCF.txt"
copy "%ScriptsPath%output_CCF.txt"+"%%G.txt" "%ScriptsPath%output_CCF.txt"
del "%%G.txt"
)
If you use the /F option of the FOR command, then you can use another command to generate the set of data to be iterated over.
Use this FOR statement and I believe you'll find the result you're looking for.
FOR /F "usebackq" %%G IN (`dir /ON /B *.sql *.up`) DO ...
This uses the dir command to generate the set of files to use. The /ON argument orders the files by name and the /B argument provides a bare format (no heading information or summary) of the file names.
For more details on the arguments check the help for either of the commands from the command line dir /? and for /?

Batch file which saves user input path with spaces to a text file. (win 7)

First of all i am a noob in programming.
I am trying to make a batch file which takes an installed directory of a program as user input when run for the first time (means it should not ask for the directory the second time it is run). By searching for various scripts, i reached till here,
#echo off
Echo =============================================
echo Directory
Echo =============================================
setlocal enableextensions enabledelayedexpansion
set /p mypath=Please specify install directory;
Echo %mypath% ----was what you typed
pause
echo start>temp.txt
echo %mypath%>>temp.txt
echo \programfolder\program.exe>>temp.txt
echo -argument -argument>>temp.txt
setlocal enabledelayedexpansion
set FINAL=
for /f %%a in (temp.txt) do (
set FINAL=!FINAL!%%a
)
echo %FINAL%>input.txt
del /q temp.txt
Pause
start "<input.txt"
This saves the input path in the "input.txt" text file, and runs the program the next time it is launched.
I want the text file to have the saved path as "start driveletter:\foldername\foldername with spaces\programfolder\program.exe" -arguments
However the "start", "program folder", "program.exe" and "-arguments" are fixed.
The user input path should get saved in- %mypath%.
The does what you asked, I think:
#echo off
if exist "go.bat" go.bat
set /p "mypath=Please specify install directory; "
Echo "%mypath%" ----was what you typed
pause
>"go.bat" echo #start "" "%mypath%\programfolder\program.exe" -argument -argument

Windows Batch: Automatically upload local CSV file list via FTP

I built a script that reads a CSV file for a list of files and then uploads them to an FTP server. The CSV structure is like this:
local_file,remote_file
The script creates a text file with all the necessary FTP commands and then runs the FTP command. Everything works, except that the for loop executes code that is outside of it's command, i.e., everything below echo put "%1" "%2" >> %Commands% is also executed on every for loop, and instead of getting a nicely formatted file with all the put commands I get this (from the commands file output):
open servername
username
password
binary
put "local_path_to\first_file_on_the_list.php" "remote_path_to\first_file_on_the_list.php"
close
bye
put "-d" "-i"
close
bye
put "-d" "-i"
close
bye
put "-d" "-i"
close
...
Here is the script code:
#echo off
setlocal EnableExtensions
rem Connection credentials
set Server=servername
set UserName=username
set Password=password
set Commands="commands.txt"
echo open %Server% > %Commands%
echo %UserName% >> %Commands%
echo %Password% >> %Commands%
echo binary >> %Commands%
rem Read the CSV file line by line
for /f %%a in (matches3.csv) do call :parse %%a
rem Transform CSV line into FTP put commmand
:parse
echo put "%1" "%2" >> %Commands%
:end
rem Add commands to close ftp conn
echo close >> %Commands%
echo bye >> %Commands%
rem Perform the FTP upload
echo loggin in to ftp...
FTP -d -i -s:%Commands% %Server%
echo finished.
pause
rem Clean up.
if exist %Commands% del %Commands%
endlocal
exit
I don't understand why everything below :end is getting executed!
Thank you very much in advance
Why shouldn't the code below :end execute? You never put anything in the script to end the :parse routine.
The first ewall answer should almost work, except you need a GOTO :END after the FOR statement. I don't see how a proper implementation of his suggestion can lead to an endless loop.
Another option is to simply move your subroutine after your EXIT statement.
You have other hidden problems. File paths/names can contain spaces, so the default FOR delimiter of space,tab will not preserve the entire line if there are spaces. The default EOL will also cause any line that begins with ; to be ignored. That is a potential (but unlikely) problem, because a valid filename can begin with ;.
The solution is to set EOL to a character that cannot begin a valid file spec, and set DELIMS to nothing: "EOL=: DELIMS="
It is much more efficient to enclose all your file writing lines within parentheses and redirect the output just once. It's also easier to write and looks better.
Edit - The original script was attempting to connect to the server in both the ftp script and the ftp command line. One or the other had to be removed. I removed it from the ftp script.
#echo off
setlocal EnableExtensions
rem Connection credentials
set Server=servername
set UserName=username
set Password=password
set Commands="commands.txt"
(
echo %UserName%
echo %Password%
echo binary
rem Read the CSV file line by line
for /f "eol=: delims=" %%a in (matches3.csv) do call :parse %%a
rem Add commands to close ftp conn
echo close
echo bye
)>%Commands%
rem Perform the FTP upload
echo logging in to ftp...
FTP -d -i -s:%Commands% %Server%
echo finished.
pause
rem Clean up.
if exist %Commands% del %Commands%
endlocal
exit
rem Transform CSV line into FTP put commmand
:parse
echo put "%1" "%2"
The :end doesn't stop the CALL function from running, it's just a label. Instead, I suspect you meant to use GOTO :eof, which would return to the for-loop:
...
rem Read the CSV file line by line
for /f %%a in (matches3.csv) do call :parse %%a
rem Transform CSV line into FTP put commmand
:parse
echo put "%1" "%2" >> %Commands%
goto :eof
rem Add commands to close ftp conn
...
Or, rather than using a call, you may find it a lot simpler to use parenthesis, like this:
...
rem Read the CSV file line by line
for /f %%a in (matches3.csv) do (
rem Transform CSV line into FTP put command
echo put "%%a" "%%b" >> %Commands%
)
rem Add commands to close ftp conn
...
maybe this works
#echo off
setlocal EnableExtensions
rem Connection credentials
set Server=servername
set UserName=username
set Password=password
set Commands="commands.txt"
echo open %Server% > %Commands%
echo %UserName% >> %Commands%
echo %Password% >> %Commands%
echo binary >> %Commands%
rem Read the CSV file line by line
for /f %%a in (matches3.csv) do call :parse %%a
rem Transform CSV line into FTP put commmand
:parse
echo put "%1" "%2" >> %Commands%
:end
rem Add commands to close ftp conn
echo close >> %Commands%
echo bye >> %Commands%
rem Perform the FTP upload
echo loggin in to ftp...
FTP -d -i -s:%Commands% %Server%
echo finished.
pause
rem Clean up.
if exist %Commands% del %Commands%
endlocal
exit /b
rem Transform CSV line into FTP put commmand
:parse
echo put "%1" "%2" >> %Commands%
exit /b
:end
rem Add commands to close ftp conn
echo close >> %Commands%
echo bye >> %Commands%
exit /b

Looping folder names to a variable using Batch?

I'm wanting to change the icon of every sub folder in a particular directory, can I do this by means of a loop similar to the %%~nxf used for files (I’m after folders not files)
attrib -h -r C:\ICT\AutoCAD_2010\Customisations\AO2\EXE\desktop.ini
echo [.ShellClassInfo] >C:\ICT\AutoCAD_2010\Customisations\AO2\EXE\desktop.ini
echo IconFile= C:\ICT\AutoCAD_2010\Customisations\AO2\ICO\FolderIcon.ico >>C:\ICT\AutoCAD_2010\Customisations\AO2\EXE\desktop.ini
echo InfoTip=EXE file location... >>C:\ICT\AutoCAD_2010\Customisations\AO2\EXE\desktop.ini
echo IconIndex=0 >>C:\ICT\AutoCAD_2010\Customisations\AO2\EXE\desktop.ini
attrib +h +r C:\ICT\AutoCAD_2010\Customisations\AO2\EXE\desktop.ini
attrib +r C:\ICT\AutoCAD_2010\Customisations\AO2\EXE
Of cause do this a couple of hundred times in a batch and well you get the idea.
You may process folders (not files) with /D switch of for command:
for /D %%a in (*) do echo Folder: %%a
You may also combine /R with /D to process every subfolder beneath the given path. For further details, type: for /?