Batch script to read/write data using COM port - bash

I need to write a batch script that can read and write to a COM port (in my case, COM1).
I know that I can send data to a COM port using
echo hello > COM1
And I can read data coming in to a text file using
type COM1 > sample.txt
I'd like to be constantly capturing the data coming into the COM port into a text file and be able to send commands to the COM port depending on what data was just read on the COM port. Is there a way to do this in a batch script?
Would it be easier/better to do it in a bash script? How would I do it in bash?
EDIT:
The data I wish to read is the output of the boot of a system to an UEFI shell. Then, when it gets there, enter some commands and capture the output of those commands. Then, boot an OS (capturing the output of the boot), enter some more commands to the Linux shell and capture the output of them. It does not contain an exact number of characters.
If a bash script is the only answer, that is ok. I can work with that. I am currently running version 4.1.10(4) of bash in Windows 7 using cygwin.

Playing around with a few different things with batch scripts leads me to believe nothing is going to work in with a batch script and the standard Windows command line tools. set /P returns immediately when reading from COM1, and something like type COM1 will copy the serial data in large chunks line by line.
I had better luck using Cygwin bash. Here's a simple script that receives lines from COM1 and echoes them back. It exits when it receives a line starting with "quit". You can test it out by using a terminal emulator on the other end of the serial link or just using statements like echo quit > COM1.
CR="$(echo -e '\r')"
exec 4<> /dev/com1
cat <&4 | while :
do
IFS="$CR" read -r line
case "$line" in
quit*)
echo "goodbye$CR" >&4
break
;;
*)
echo "recieved line: $line"
echo "recieved line: $line$CR" >&4
;;
esac
done
The CR variable holds a carriage return character which in this example is used to strip it off the input lines and used to terminate lines with CR LF when outputting them over the serial line. Depending how exactly your BIOS behaves you may or may not need to do this in your own script.
The exec 4<> /dev/com1 line is crucial. This opens the COM port once for both reading and writing. Windows only allows a COM port to be open once, so if this wasn't done it wouldn't be possible to read and write to the COM port. The 4 means that is assigned to file descriptor 4 and the exec statement keeps it open for the rest of the script.
The cat <&4 | part is also important. Unfortunately there seems to be a bug in Cygwin bash where it will try to rewind the file descriptor if it reads past the end of a line. This works for files, but it doesn't for serial ports so data gets lost. To workaround this problem the script reads from a pipe instead, which bash is smart enough to not try to rewind.
The reason for setting IFS="$CR" is to strip off the carriage return at the end of a line as mentioned before, and to not strip off anything while reading. The read command uses the IFS string to break up the input line into words. You may be able to use this to your advantage and set it to a different value to make it easier to parse the BIOS output.
The rest of the details are pretty straightforward. The -r option for read causes it not to treat \ characters specially. Depending on what sort of line endings your BIOS expects you have three different ways you can write your echo statements:
echo "Both CR and LF line ending$CR" >&4
echo -n "CR only line ending$CR" >&4
echo "LF only line ending" >&4
One thing this script doesn't do it set the COM port parameters like baud rate and flow control. This is probably best done using the normal MODE COM1 command. Cygwin has an equivalent stty command, but it doesn't appear to support all the parameters.
Another entirely different option is to use Expect. If you find that it's hard to get bash to parse and respond appropriately to your BIOS's output then you might consider using that instead. This sort of thing is what its designed for, though there's a bit of learning curve if you're not already familiar with TCL. It's available as a standard Cygwin package.

The standard way to read data from the serial port is not:
type COM1 > sample.txt
nor:
copy COM1 sample.txt
It would be if that data would have a standard EndOfFile mark, like a Ctrl-Z character.
If that data comes in lines that ends in CR+LF or just CR characters, then the standard way to read them would be:
set /P "var=" < COM1
If that data have not a delimiter, like CR or CR+LF, then you must specify the exact format of such data:
Do you want to read records of a fixed number of characters?
Do you want to read characters until a certain one appear? Which one?
May the input data contain control characters? Which ones?
Any other format of the input data?
EDIT: Reply to the comments
When set /P command is executed with redirected input, it does not wait for data. In this case, it returns immediately and the variable is not changed.
We may try to wait for the next character received from the serial port and then execute the set /P command; this way, the set /P should correctly read an input line terminated in CR or CR+LF that does not contain control characters.
This is the code:
set "key="
for /F "delims=" %%K in ('xcopy /W "%~F0" "%~F0" ^< COM1 2^> NUL') do (
if not defined key set "key=%%K"
)
set /P "line=" < COM1
set "line=%key:~-1%%line%"
echo Line read from COM1: "%line%"
Try it and report the result. Sorry, I can not try input read from COM1 in my computer.

Related

How does one close stdin from the windows CMD prompt?

I know that one can close stdin when executing a command from a Linux CLI using logic such as:
0<&- <command>
This simulates/emulates running an application unattended, or within a CI/CD system such as Jenkins/Gitlab that has stdin closed.
Why do I care? Because we have an application that has a "Press any key to continue..." prompt.
When run under Jenkins/Gitlab or anything that doesn't have stdin open, it just moves on..... when run on Linux.
How would I do this, and is this possible to do this from the Windows CMD Window CLI?
I've tried 0<&- and that results in the message
0<& was unexpected at this time.
Google search gives many hits for stdin but all the documentation is on redirection. I haven't found anything for windows for closing.
You can try
command <nul
which basically means "take any input from the NUL device (as an infinite source of "CRLF"'s). This works as long as command takes its input from STDIN (like the pause command) and doesn't flush the input buffer (like the choice command)
If you need textual input (like the set /p command), you need another approach:
echo inputtext|command

DOS Batch File Command does not execute

The following command line executes properly:
MagBoltz32 < input.txt > out.txt
The executable requires the input in brackets as above and outputs the text file. The following "batch.BAT" file (now including the escape characters) does NOT execute but simply hangs as if expecting more input.
MagBoltz32 ^< input.txt ^> out.txt
Executing batch.BAT simply results in a blinking cursor. Ultimately this command line will go into FOR loop which loops over several input/output files. I do have access to the source code.
That's not "input in brackets". The programm doesn't use any parameters (at least not in the shown syntax). It gets it's input from STDIN and writes to STDOUT.
<input.txt redirects the content of input.txt to STDIN, so the program is able to use it, like it were entered per keyboard.
>output.txt redirects STDOUT to the file output.txt instead of writing it to the screen.
Escaping characters is only needed, if you want the special char to be shown on the screen instead of "executing" it (simply spoken), so in your case, escaping the redirection characters makes no sense, but disables the redirection.
In some OS's you need to escape the pipes <, > and | so the syntax could be completely valid. This is particularly true for batch files (http://www.robvanderwoude.com/redirection.php) ... Have you checked to make sure all the files are in the same path that is executing the batch file? If you run the batch from C:\foo even though bath.BAT is in c:\bar, the input.txt will be expected to be in C:\foo.

What does a forward slash before a pipe in cmd do to remove the line ending of an echo?

This code:
#echo off
echo/|set /p ="Executing backup...."
echo/|set /p =" backup procedure"
... came from Echoing in the same line and produces the below output in a cmd window:
Executing backup....backup procedure
However, I cant seem to find an explanation through google on what the forward slash does to the ¿pipe? to cause set's output to be echoed to the console / stdout
If anyone could also suggest a good website for learning more about cmd / cmd programs' features like this, it would be appreciated.
The echo/ is simply a way of printing only an empty line, instead of ECHO IS ON for a single echo.
But in this case it's completly unimportant, as the only use of the echo is for creating some stuff for the pipe, so the set /p will not wait for user input.
But this way to echo text without a linefeed is very inefficient, as a pipe creates two new instances of cmd.exe.
It's much simpler and faster to use
<nul set /p "=My Text"
The redirect from NUL will also stop the waiting for user input.
Care of https://stackoverflow.com/users/1201210/tenterhook
1) echo prints the result of set /p =... with or without the / before the pipe, so I'm not sure what your question is asking
2) (It will also print set /p =... with random junk after the echo, too, since it's reading the piped stuff and not the arguments it receives.)
I will hence suggest edits to the referenced SO post, to prevent others confusion.

Use parameter as intermediate input in Batch File

In a batch file, I am running exe which takes input from user. I want to hardcode value and continue process. check following example:
In Bat File(GetData.bat):
set /p UserInput = Enter a number?
%1
To call bat file:
GetData 5
but it's waiting for input instead of setting 5.
Note: It's just for example actually i am calling exe which takes input in process. I can't modify exe.
Command line parameters are not the same thing as standard input (stdin). There are some programs that can accept input both as command arugments or via stdin, but that is not the norm.
To get your batch script to work, you must ECHO the data to stdout, and then pipe the results to GetData.bat
echo 5|GetData
If you must provide multiple lines of input, then you can do something like
(
echo line1
echo line2
echo line3 etc.
) | yourProgram.exe
Or you can put the input in a text file and then pipe the contents of the file to your command.
type commands.txt | yourProgram.exe
Or you can redirect the input to your file
<commands.txt yourProgram
Note that some programs read the keyboard directly, and or flush the input buffer before prompting for input. You will not be able to pipe input into such a program.
Update - exe not using piped input
Most database command line programs that I have seen have an option to specify the username and password as command line arguments. You should read the documentation, or google your exe for command line arguments.
If your exe does not have an option to specify the values on the command line, then your only option is to use a 3rd party tool like AutoHotKey that can be scripted to pass keystrokes to a running program.
Look at this example:
echo Enter a number?
set UserInput=%1
echo %UserInput%
Then if you type "GetData 5", the output will look like this:
Enter a number?
--empty line to execute "set"
5

Send commands to other command-line programs

Is there a way, to send commands to another command-line program?
'Cause i have a special command-line program, but I can't send commands to it using syntax like program.exe something_to_do
the program executes something like this: ("here syntax" is where i want to input text to and also enter to start)
TheWhateverCommandLineProgram
Version 1.1
Give an option: "here syntax"
the program in code looks something like this:
echo TheWhateverCommandLineProgram
echo Version 1.1
Set opt=
set /p opt=Give an option:
if %opt%==command1 goto com1
if %opt%==command2 goto com2
...
Well, i guess so cause it wasnt me who made it (btw: off course its not called TheWhateverCommandLineProgram)
If you just want to give keyboard input to a commandline program you can just use echo and pipe it:
echo some text | program.exe
If you need more lines, then write them to a file and use input redirection:
echo one line > file
echo second line >> file
program.exe < file
I'm not 100% sure I understand what you're looking for. Here's two options:
You have two windows, each running a batch program. Let's say they are called myscript1.bat and myscript2.bat. You want to send a set of commands from myscript1.bat to be executed by myscript2.bat
You have a single batch script named myscript.bat, which executes a single program named program.exe. You want program.exe to execute some commands, or do some something.
Are either of these what you're looking for? Here's some idea:
Make myscript1.bat create a third file, mycommands.bat. Once myscript2.bat sees the file mycommands.bat exists, it will execute it and delete it. (Wow. Lame.)
Use Windows Scripting Host command (it's built in to Windows since Win2K) or Powershell (usually on most computers nowadays, if they have been updated). Either of these can send keystrokes to another program. Using those keystrokes, you can control the other program.
In what form does the other program take input? From the command prompt?
If the latter then I recommend Autohotkey: http://www.autohotkey.com/
You can use Autohotkey as a bridge and it will send the command as keypresses to the window of the other batch file.
You can ask for help in their forum. They are quite helpful.

Resources