run commands into openssl through batch - powershell

i am trying to set up a custom script in uptime monitoring and have this command run openssl and have arguments that i pass into it run.
openssl s_client -CAfile C:\apcerts\certs\ -quiet -connect ${HOST}:${PORT} > ${TMPF} 2>&1 < EOF
<TF80DOC><XPLN/></TF80DOC>
EOF
if (Select-String "Update Level" ${TMPF} > /dev/null)
{
exitstatus=$STATE_OK
Select-String "Update Level" ${TMPF} | sort | uniq}
elseif (Select-String "Regulatory" ${TMPF} > /dev/null)
{
exitstatus=$STATE_OK
Select-String "Regulatory" ${TMPF} | sort | uniq}
else{
echo `date` >> /tmp/caught_errs.out
cat ${TMPF} >> /tmp/caught_errs.out
echo " " >> /tmp/caught_errs.out
exitstatus=$STATE_CRITICAL
}
rm -f ${TMPF} 2> /dev/null
exit ${exitstatus}
i want to have the variables ${host}:${port} are left blank and i want to have an argument that i manually put information in and the fields populate with that information.
for example i need to connect to blank-xml.myinfo.com:30011.
the problem i am running into is when i set this up on the custom monitors i have a .bat that opens openssl but cannot open the .txt file to run commands given.
what do i need to do in order for this to work.
Update:
I have made a batch file that passes in information to openssl that is a lot smaller.
#echo off
c:\OpenSSL-Win64\bin\openssl s_client -connect help-xml.helpme.com:443
this section works great shows information on the screen that is needed. I need to send in another command to the window also but get a error stating the the < command isn't an executable or batch process.
that command is <TF80DOC><XPLN/></TF80DOC> i have tried using the & symbol and have used echo before it but still am getting the same error or the screen will pop up and close instantly with no information.
the if then statement works after i run <TF80DOC><XPLN/></TF80DOC> since this has information that is displayed the statement is looking for. But if i can't get the <TF80DOC><XPLN/></TF80DOC> to be sent to openssl after the s_client -connect help-xml.helpme.com:443 runs then the if statement will never work.
Update:
I have changed the powershell command to pipe in the command after s_client -connect help-xml.helpme.com:443
the new code looks like
#'
<TF90DOC><XPLN/></TF90DOC>
'# | C:\OpenSSL-Win64\bin\openssl s_client -quiet -connects_client -connect help-xml.helpme.com:443 > test1.txt 2>&1
the if then statement isn't an issue since i know how to fix that part of it. the powershell part of the code works but requires me to press enter which is not what i need it to do. i need it to execute the command automatically without user input
For the batch command i have made some slight changes to it, which are
#echo off
setlocal enabledelayedexpansion
set "var=<TF90DOC><XPLN/></TF90DOC>"
echo echo !var! | C:\OpenSSL-Win64\bin\openssl s_client -connect tf90-xml.bsi.com:443> test1.txt 2>&1
this command still gives me the error
< was unexpected at this time.

I completely misunderstood your question at first and didn't realize you needed to send the command to the newly-opened openssl instance. In order to do this, you need to pipe the command you want to openssl.
#echo off
echo ^<TF80DOC^>^<XPLN/^>^</TF80DOC^>|c:\OpenSSL-Win64\bin\openssl s_client -connect help-xml.helpme.com:443
Note that this is untested, and you may have to also escape the escape characters:
echo ^^^<TF80DOC^^^>^^^<XPLN/^^^>^^^</TF80DOC^^^>|c:\OpenSSL-Win64\bin\openssl s_client -connect help-xml.helpme.com:443
If you need to send more than one command, place them in a separate batch file with each command preceded by an echo and pipe that to the openssl command, like this:
commands.bat
#echo off
echo echo This is one command.
echo echo This is another command.
main.bat
#echo off
commands.bat|C:\OpenSSL-Win64\bin\openssl s-client -connect help-xml.helpme.com:443

Try this: (See edit below.)
#echo off
(
echo(^^^<TF90DOC^^^>^^^<XPLN/^^^>^^^</TF90DOC^^^>
echo;
) | C:\OpenSSL-Win64\bin\openssl s_client -connect tf90-xml.bsi.com:443 > test1.txt 2>&1
Edit:
From what you describe, it sounds like openssl is not ready for input immediately after entry of that XML, and is black holing the newline piped to stdin. So rather than dumping everything to stdin at once, you'll need to introduce a sleep of some sort, and listen to that program's stdout for the appropriate prompt.
Here's a proof of concept in JScript that I think can be applied for you as soon as I know what text openssl uses to prompt for the XML, and then for the Enter keypress. This is a similar method as described on the WSHScriptExec StdOut Property documentation page, and is more flexible than any solution attempted thusfar.
test.bat
#if (#a==#b) #end /* begin multiline JScript comment
:: batch portion
#echo off
setlocal
cscript /nologo /e:JScript "%~f0"
goto :EOF
:: end batch portion / begin JScript */
var osh = new ActiveXObject('wscript.shell'),
fso = new ActiveXObject('scripting.filesystemobject'),
log = fso.CreateTextFile('output.txt', true),
read;
var exe = osh.Exec('cmd /c test2.bat');
while (1) {
if (!exe.StdOut.AtEndOfStream) {
read += exe.StdOut.Read(1); // read 1 char at a time
if (/Arg 1:/.test(read)) {
WSH.Sleep(50);
exe.StdIn.Write('<TF80DOC><XPLN/></TF80DOC>\n');
WSH.Sleep(100);
// This is the output we wish to capture.
read = exe.StdOut.ReadLine();
WSH.Echo(read);
log.WriteLine(read);
read = '';
} else if (/Press any key/.test(read)) {
WSH.Sleep(100);
exe.StdIn.Write('\n');
break;
}
}
else WSH.Sleep(100);
}
log.Close();
while (!exe.Status) WSH.Sleep(100);
WSH.Echo('Done.');
test2.bat
#echo off
setlocal enabledelayedexpansion
set /P "input=Arg 1: "
echo(Input: !input!
pause

Related

Avoid Line break at end of cmd output?

when I use this command pwsh -c echo hello in cmd I get the following output:
C:\>pwsh -c echo hello
hello
C:\>
I do not get that line break at the end
when I run it on powershell:
PS C:\> pwsh -c echo hello
hello
PS C:\>
So I think the problem is in cmd. I know this is not such a problem and have an easy fix but I have some programs uses cmd to access powershell and removing that line break is not that fun.
So is there any fix to prevent cmd to add that line ?
Mofi has provided the crucial pointers in comments:
When executing a command interactively, cmd.exe unconditionally appends a a newline (line break) to the command's output, presumably for readability and perhaps also to ensure that the next prompt always starts on a new line.
This applies irrespective of what that command is. In other words: It doesn't matter that your command happens to be a PowerShell command.
However, that trailing newline does not become part of the command's output, therefore programmatic processing of a command's output is not affected, such as when you redirect > to a file or process the output lines one by one with for /f.
In other words: for programmatic processing you need not remove the trailing newline, because it isn't part of the actual command output.
Conversely, if you really need to in effect suppress the trailing newline for display, you'll have to modify the command's output - if that is even an option - so that the output itself doesn't end in a newline, as shown in this SuperUser answer for cmd.exe's own echo command; for PowerShell, you could do pwsh -c Write-Host -NoNewLine hello.
Edge case:
When capturing output from a batch file that is running without #echo off (or with echo on) - in which case the trailing newlines do become part of the output - you can filter out empty lines by piping to findstr /r /v /c:"^$" (as also shown in the linked answer); e.g.
foo.cmd | findstr /r /v /c:"^$"
However, note that all empty lines are filtered out this way - potentially including actual empty lines in the output from commands executed by the batch file.
If preventing that is required, a more sophisticated approach is required, which, however (a) relies on the standard prompt string (e.g., C:\>) being used and (b) can still yield false positives:
foo.cmd | powershell -nop -c "#($Input) -join \"`n\" -replace '\n(?=[a-z]:\\.*?>)'"
Finally note that if you execute the above commands without capturing or redirecting their output, their overall output in the cmd.exe console will again have a trailing newline.

User input as a command line argument

I have a program BIL.exe that prompts for user four inputs (marked with pink below).
I would like to run this program from command line by specifying the inputs as arguments. Something like this:
.\BIL.exe "HOAL" 78 80 1
In a batch script I can do it as shown below. However this doesn't work in command line.
#echo off
set var1="HOAL"
set var2=78
set var3=80
set var4=1
(echo %var1% && echo %var2% && echo %var3% && echo %var4%)|BIL.exe
Does the solution differ between CMD and powershell?
So given your confirmation that stdin from file worked means that the solution should be simple:
batch_name.cmd
#echo off
(echo %1&echo %2&echo %3&echo %4)|bil.exe
it sends the output in list format. so you should be able to run the batch as:
batch_name.cmd "HOAL" 78 80 1
keep in mind, I am not stripping outside quotations, as it seems that those are used by the executable. If not, then change to:
#echo off
(echo %~1&echo %~2&echo %~3&echo %~4)|bil.exe
As you never need to set anything in relation to the variable name, we simply do not use variables as they come as input meta variables from your input.

Multiple Powershell command needs to be run from command line

i need to run single line powershell script like this one:
$a = Get-Content test.tmp; $b = [timespan]::fromseconds($a); "{0:HH:mm:ss,fff}" -f ([datetime]$b.Ticks)
in command line. When running it directly from powerhsell cli it work fine. but when trying run:
powershell "$a = Get-Content test.tmp; $b = [timespan]::fromseconds($a); "{0:HH:mm:ss,fff}" -f ([datetime]$b.Ticks)"
from command line cli i get error. I cannot run it from script in form of .ps1 file as i am not allowed to change restriction policy regarding to run powershell script.
Anybodoy would be able to point me what i have to change to run it properly from command line?
Many thanks
With proper quoting / parentheses you need no intermediate variables and only one command:
'{0:HH:mm:ss,fff}' -f ([datetime]([timespan]::fromseconds((Get-Content test.tmp))).Ticks)
In a batch:
powershell -C "'{0:HH:mm:ss,fff}' -f ([datetime]([timespan]::fromseconds((Get-Content test.tmp))).Ticks)"
Or to get the duration into a batch variable:
:: Q:\Test\2017\09\23\SO_46379453.cmd
#Echo off
For /f "tokens=1*" %%A in (
'powershell -C "\"{0:HH:mm:ss,fff}\" -f ([datetime]([timespan]::fromseconds((Get-Content test.tmp))).Ticks)" '
) Do Set "Duration=%%A,%%B"
set Duration

How to check in command-line if a given file or directory is locked (used by any process)?

I need to know that before any attempt to do anything with such file.
Not sure about locked directories (does Windows have that?)
But detecting if a file is being written to by another process is not difficult.
#echo off
2>nul (
>>test.txt echo off
) && (echo file is not locked) || (echo file is locked)
I use the following test script from another window to place a lock on the file.
(
>&2 pause
) >> test.txt
When I run the 2nd script from one window and then run the 1st script from a second window, I get my "locked" message. Once I press <Enter> in the 1st window, I get the "unlocked" message if I rerun the 1st script.
Explanation
Whenever the output of a command is redirected to a file, the file of course must be opened for write access. The Windows CMD session will attempt to open the file, even if the command does not produce any output.
The >> redirection operator opens the file in append mode.
So >>test.txt echo off will attempt to open the file, it writes nothing to the file (assuming echo is already off), and then it closes the file. The file is not modified in any way.
Most processes lock a file whenever they open a file for write access. (There are OS system calls that allow opening a file for writing in a shared mode, but that is not the default). So if another process already has "test.txt" locked for writing, then the redirection will fail with the following error message sent to stderr - "The process cannot access the file because it is being used by another process.". Also an error code will be generated upon redirection failure. If the command and the redirection succeed, then a success code is returned.
Simply adding 2>nul to the command will not prevent the error message because it redirects the error output for the command, not the redirection. That is why I enclose the command in parentheses and then redirect the error output to nul outside of the parens.
So the error message is effectively hidden, but the error code is still propagated outside of the parens. The standard Windows && and || operators are used to detect whether the command inside the parens was successful or failed. Presumably echo off will never fail, so the only possible reason for failure would be the redirection failed. Most likely it fails because of a locking issue, though technically there could be other reasons for failure.
It is a curious "feature" that Windows does not set the %ERRORLEVEL% dynamic variable to an error upon redirection failure unless the || operator is used. (See File redirection in Windows and %errorlevel%). So the || operator must read the returned error code at some low level, not via the %ERRORLEVEL% variable.
Using these techniques to detect redirection failure can be very useful in a batch context. It can be used to establish locks that allow serialization of multiple events in parallel processes. For example, it can enable multiple processes to safely write to the same log file at the "same" time. How do you have shared log files under Windows?
EDIT
Regarding locked folders. I'm not sure how Windows implements this, perhaps with a lock. But if a process has an active directory involving the folder, then the folder cannot be renamed. That can easily be detected using
2>nul ren folderName folderName && echo Folder is NOT locked || echo folder is LOCKED
EDIT
I have since learned that (call ) (with a space) is a very fast command without side effects that is guaranteed to succeed with ERRORLEVEL set to 0. And (call) (without a space) is a fast command without side effects that is guaranteed to fail with ERRORLEVEL 1.
So I now use the following to check if a file is locked:
2>nul (
>>test.txt (call )
) && (echo file is not locked) || (echo file is locked)
In addition to great answer from dbenham, the following form finally help me understand used technique:
( type nul >> file.txt ) 2>nul || echo File is locked!
type nul command gives an empty output and does not affect the current echo setting like echo off command in orginal.
If you want to use if–then–else condition remember of correct order - success statement (&&) is going first and alternate statement (||) is going second:
command && (echo Command is successful) || (echo Command has failed)
If you download and install the Windows Server 2003 Resource Kit Tools there is a utility called oh.exe that will list open file handles for a given file:
http://www.microsoft.com/en-us/download/details.aspx?id=17657
Once you install it, reboot your machine and you'll be able to use the utility. You can see all the options in the Help and Support center as well as by typing oh /? in the command prompt.
(Info from : http://windowsxp.mvps.org/processlock.htm )
Note, the writing of a message stating the file status was less helpful than a batch command that set a return code. For example, return code 1 if file is locked.
#echo off
2>nul (
>>test.tmp echo off
) && (EXIT /B 0) || (EXIT /B 1)
Other answers resulted in side-effects for me. For instance, the following from this answer will cause file watchers to trigger:
COPY /B app.exe+NUL app.exe
And the following from the top answer here would overwrite any changes made to the target file:
2>nul (
>>test.txt (call )
) && (echo file is not locked) || (echo file is locked)
On modern version of Windows, you can call into Powershell to accomplish this task with zero side-effects:
powershell -Command "$FileStream = [System.IO.File]::Open('%FILE%', 'Open', 'Write'); $FileStream.Close(); $FileStream.Dispose()" && (echo File is not locked) || (echo File is locked)
This will not modify the file or its metadata at all, even when it isn't locked.
Example usage
I use this method in my custom git mergetool script for merging Excel files. The way a git mergetool works is that it waits for the script shell to exit, then checks if the target file was modified, prompting with "XX.yyy seems unchanged. Was the merge successful [y/n]?" if it wasn't. However, Excel (at least the version I'm using) does not spawn a new process for each file it opens. So if Excel is already open, the script will exit immediately, and git will detect no changes to the file, resulting in that prompt.
So I devised the method above, and I use it like below:
REM block until MERGED is closed
:loop
powershell -Command "$FileStream = [System.IO.File]::Open('%MERGED%', 'Open', 'Write'); $FileStream.Close(); $FileStream.Dispose()" >NUL 2>NUL || (goto :loop)
Incidentally, dbenham's solution also seems to be an effective way to find out if a process is running. It was the best solution I found for the following application:
start /b "job1.exe >> job1.out"
start /b /wait "job2.exe >> job2.out"
::wait for job1 to finish using dbenham's code to check if job1.out is in use
comparejobs.exe
Just i want to share with you an example of my script based on #dbenham's trick
Description of this script : Check_Locked_Files.bat :
This script can scan and check for locked files on a set of folders that can be modified into the script; for example, i have chosen those set of folders to be scanned :
Set Folders=^
^ "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%UserProfile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%ProgramFiles%\Internet Explorer"^
^ "%ProgramFiles%\Skype"^
^ "%ProgramFiles%\TeamViewer"^
^ "%WinDir%\system32\drivers"^
^ "%Temp%"
The output result is in HTML format for more readability.
If the file is locked we show it in red color otherwise we show it in green color.
And the whole script is : Check_Locked_Files.bat
#echo off
Rem This source is inspired from here
Rem hxxps://stackoverflow.com/questions/
Rem 10518151/how-to-check-in-command-line-if-a-given-file-or-directory-is-locked-used-by-any?answertab=active#tab-top
Rem Thanks for dbenham for this nice trick ;)
Mode con cols=90 lines=5 & color 9E
Title Scan and Check for Locked Files by Hackoo 2017
set "LogFile=%~dp0%~n0.html"
(
echo ^<html^>
echo ^<title^> Scan and Check for locked files by Hackoo 2017^</title^>
echo ^<body bgcolor^=#ffdfb7^>
echo ^<center^>^<b^>Log Started on %Date% # %Time% by the user : "%username%" on the computer : "%ComputerName%"^</b^>^</center^>
)> "%LogFile%"
echo(
echo --------------------------------------------------------------------------
echo Please Wait a while ....... Scanning for locked files is in progress
echo --------------------------------------------------------------------------
Rem We Play radio just for fun and in order to let the user be patient until the scan ended
Call :Play_DJ_Buzz_Radio
Timeout /T 3 /nobreak>nul
cls
Set Folders=^
^ "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%UserProfile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"^
^ "%ProgramFiles%\Internet Explorer"^
^ "%ProgramFiles%\Skype"^
^ "%ProgramFiles%\TeamViewer"^
^ "%WinDir%\system32\drivers"^
^ "%Temp%"
#For %%a in (%Folders%) Do (
( echo ^<hr^>^<font color^=DarkOrange^>^<B^>Folder : %%a^</B^>^</font^>^<hr^>) >> "%LogFile%"
#for /f "delims=" %%b in ('Dir /A-D /s /b "%%~a\*.*"') do (
Call :Scanning "%%~nxb"
Call:Check_Locked_File "%%~b" "%LogFile%"
)
)
(
echo ^<hr^>
echo ^<center^>^<b^>Log ended on %Date% # %Time% on the computer : "%ComputerName%"^</b^>^</center^>
echo ^</body^>
echo ^</html^>
)>> "%LogFile%"
Start "" "%LogFile%" & Call :Stop_Radio & exit
::***********************************************************************************
:Check_Locked_File <File> <LogFile>
(
2>nul (
>>%1 (call )
) && ( #echo ^<font color^=green^>file "%~1"^</font^>^<br^>
) || (
#echo ^<font color^=red^>file "%~1" is locked and is in use^</font^>^<br^>
)
)>>%2 2>nul
exit /b
::***********************************************************************************
:Scanning <file>
cls
echo(
echo --------------------------------------------------------------------------
echo Please Wait a while... Scanning for %1
echo --------------------------------------------------------------------------
exit /b
::***********************************************************************************
:Play_DJ_Buzz_Radio
Taskkill /IM "wscript.exe" /F >nul 2>&1
Set "vbsfile=%temp%\DJBuzzRadio.vbs"
Set "URL=http://www.chocradios.ch/djbuzzradio_windows.mp3.asx"
Call:Play "%URL%" "%vbsfile%"
Start "" "%vbsfile%"
Exit /b
::**************************************************************
:Play
(
echo Play "%~1"
echo Sub Play(URL^)
echo Dim Sound
echo Set Sound = CreateObject("WMPlayer.OCX"^)
echo Sound.URL = URL
echo Sound.settings.volume = 100
echo Sound.Controls.play
echo do while Sound.currentmedia.duration = 0
echo wscript.sleep 100
echo loop
echo wscript.sleep (int(Sound.currentmedia.duration^)+1^)*1000
echo End Sub
)>%~2
exit /b
::**************************************************************
:Stop_Radio
Taskkill /IM "wscript.exe" /F >nul 2>&1
If Exist "%vbsfile%" Del "%vbsfile%"
::**************************************************************
:: Create the file Running.tmp
ECHO %DATE% > Running.tmp
ECHO %TIME% >> Running.tmp
:: block it and do the work
(
>&2 CALL :Work 30
) >> Running.tmp
:: when the work is finished, delete the file
DEL Running.tmp
GOTO EOF
:: put here the work to be done by the batch file
:Work
ping 127.0.0.1 -n 2 -w 1000 > NUL
ping 127.0.0.1 -n %1 -w 1000 > NUL
:: when the process finishes, the execution go back
:: to the line after the CALL
In case you want to use this in a Cygwin Bash, here are the one-liners:
# To lock a file: (in a different window)
cmd.exe /C "( >&2 pause ) >> test.txt"
#Press any key to continue . . .
# To test if a file is locked (with text)
cmd.exe /C "2>nul ( >>test.txt (call ) ) && (echo ok) || (echo locked)"
#locked
# To test if a file is locked (with POSIX exit code)
cmd.exe /C "2>nul ( >>test.txt (call ) ) && (exit /b 0) || (exit /b 1)"
echo $?
#1
In case of windows network share you can try powershell command:
Get-SmbOpenFile
For example execute on file server command as administrator:
Get-SmbOpenFile | Where-Object -Property Path -match "file.txt"

How can I redirect the output of a command (running in a batch loop) to a file?

I have a windows batch file, which iterates over files in a folder and runs a command on each file. Specifically I am running xmllint to validate some files:
for %%i in (c:\temp\*.xml) do (
C:\XMLLINT\xmllint -noout -schema "C:\schemas\schema.xsd" "%%~dpnxi" >> c:\output.txt
)
It currently shows the output on screen. I want to show the output of all these commands placed in an output file. How can I achieve this? By using the append operator (>>) nothing is accomplished except for a blank file being created.
Is it because of xmllint?
If you're trying to redirect error output from the program, it might be writing to stderr. You can try to redirect it with:
for %%i in (c:\temp\*.xml) do (
C:\XMLLINT\xmllint -noout -schema "C:\schemas\schema.xsd" "%%~dpnxi" >> c:\output.txt 2>&1
)
Basically the 2>&1 at the end means redirect anything from stderr (which is 2) to stdout (which is 1). Since stdout is redirected to a file, you should now see the stderr stream in the file. Hope this works for you!
I've never used it, but if its documentation is here, have you tried just removing your "-noout" option, or adding an: "-output c:\output.txt"?