Linux - SoftwareThis forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I'm trying to write a script which will (upon completion), navigate through records maintained on an IBM mainframe using a tn3270 terminal, and scrape said records into html files for later manipulation.
Commands are emulator actions; the syntax is the same as for the right-hand side of an Xt translation table entry (an x3270 or c3270 keymap). Unlike translation tables, action names are case-insensitive, can be uniquely abbreviated, and the parentheses may be omitted if there are no parameters. Any input line that begins with # or ! is treaded as a comment and will be ignored.
However, when i call the above using the script() function in s3270, the script prints to stdout, but the actions are not executed as expected.
For example:
Code:
(NOTICE: I've shortened the script for example's sake)
$ s3270
script(/path/to/my/script/test.3270)
connect(L:ssl3270.myhost.org:2023)
ascii()
string(command1)
enter()
ascii()
L U U N N 4 24 80 0 0 0x0 -
ok
Expected output would've been (for starters) the first character of the status string changing to U (as the keyboard should unlock when i am connected), and the initial screen printed in ascii, a command sent, and the resulting screen printed in ascii as well.
I'm not sure what i am doing wrong. Can someone give me an example script that works? Something simple that depicts how i can use something like bash, perl, or python to send commands to this application and navigate through screens.
Thanks in advance
Last edited by doomed9; 11-05-2009 at 09:14 AM.
Reason: pointing out that the script i executed has been shortened from the example i gave earier in the post
This is a bash script that, if called from within s3270, will navigate to the application login page and create html files of each page along the way.
while x3270if is nice, i reckon i expected a simpler solution...something easier to use with a language's built-in functionality.
What would i do with perl? Hopefully not make a call to system() each time i want to input data. Is there a way to treat the terminal connection as a file handle and just send commands to it via print()?
Thank you for the excellent referral. I was on the right track to my answer in a few minutes...
Using perl's IPC::Run module, i was able to pipe commands directly to the s3270 subprocess. Thus, the below is an example of x3270's peer script facility:
Code:
#!/usr/bin/perl
my @s3270 = s3270;
use IPC::Run qw( start pump finish timeout );
$IPCRUNDEBUG=details;
# Incrementally read from / write to scalars.
# $in is drained as it is fed to s3270's stdin,
# $out accumulates s3270's stdout
# $err accumulates s3270's stderr
# $h is for "harness".
my $h = start \@s3270, \$in, \$out, \$err, timeout( 10 );
$in .= "connect(L:ssl3270.myhost.org:2023)\n";
$in .= "ascii\n";
$in .= "quit";
finish $h or die "s3270 returned $?";
warn $err if $err;
print $out; ## All of cat's output
hope this helps any googlers interested in scripting with s3270.
I had similar problem and, although it's an old topic, I'd like to share a bourne shell script that worked out for me (based on example script from the author of x3270 (look at sourceforge).
Amazingly, you will find NOTHING when goggling "x3270 script example". Documentation could be better and is not focused toward dummies like me. Looks like nobody does x3270 scripting, or don't care to share.
I did have a bad time trying to make this script work, hope you save your time.
Enough talking, here it goes:
Code:
#! /bin/bash
# TSO login script, which runs as a peer of x3270.
# bash version
# to disconnect user, go to
# www . efglobe . com / cgi-bin / mainframe / mainuser
set -x
me=${0##*/}
# Set up login parameters
tcp_host=fandezhi.efglobe.com
userid=your_user_id_here
password=your_pass_here
# Verbose flag for x3270if
v="-v"
# Define some handly local functions.
# x3270 interface function
function xi
{
X3270OUTPUT=6 X3270INPUT=5 x3270if 5>$ip 6<$op $v "$@"
echo `date +"%H:%M:%S:%N"` 1>&2
}
# Common x3270 Ascii function
function ascii
{
xi 'Ascii('$1')'
}
# Common x3270 String function
function string
{
xi 'String("'"$@"'")'
}
# x3270 cursor column
function cursor_col
{
xi -s 10
}
# x3270 connection status
function cstatus
{
xi -s 4
}
# Failure.
function die
{
xi "Info(\"$me error: $@\")"
xi "CloseScript(1)"
exit 1
}
#-----------------------------------------------------------------------
# loop until expected text appears on screen at given coords and
# send next command followed by enter
# Espera is Wait in portuguese, Envia is Send in portuguese
function Espera-Envia
{
texto=$1 #expected text
coord=$2 #coords from begning of texto (screen initiates at 0,0)
envia=$3 #command to send
i=1
while [ "$(ascii $coord)" != "$texto" ] && [ $i -lt 20 ]
do #sleep 0.5
xi "Wait(8000,InputField)" #man says timeout is in secs, but I thing it is in millisecs
xi "Wait(8000,Unlock)"
xi "Wait(8000,Output)"
[ $i -gt 10 ] && xi "Wait(1000,NVTMode)" #if other waits dont work, try this...
let "i +=1"
done
[ "$i" = "20" ] && die "Timeout waiting for: " $texto
#-------------------------
[ "$envia" = "" ] && return
string "$envia"
xi Enter
xi "Wait(8000,Unlock)"
}
# --- MAIN -----------------------------------------------------------
# Set up pipes for x3270 I/O
ip=/tmp/ip.$$
op=/tmp/op.$$
rm -f $ip $op
trap "rm -f $ip $op" EXIT
trap "exit" INT QUIT HUP TERM
mknod $ip p
mknod $op p
# Start x3270
x3270 -script -model 2 <$ip >$op &
xp=$!
exec 5>$ip # hold the pipe open
xi -s 0 >/dev/null || exit 1
# Connect to host
xi "Connect($tcp_host)" || die "Connection failed."
# Make sure we're connected.
xi Wait
[ "$(cstatus)" = N ] && die "Not connected."
#--- SCREEN-SCRAPING -------------------------------------
# wait initial menu, choose option TSO
Espera-Envia "Enter" "20,0,5" "TSO"
# wait prompt userid, send it.
Espera-Envia "ENTER USERID" "0,11,12" "$userid"
# wait prompt PASSWORD, send PASSWORD
Espera-Envia "Password" "7,4,8" "$password"
# Wait for "LOGON IN PROGRESS"
#add userid size to text
#typeset -i nl=18+${#userid}
#Espera-Envia "$userid LOGON IN PROGRESS" "1,1,$nl" ""
# Make sure TSO is waiting for a '***' enter
#[ "$(cursor_col)" -eq 5 ] || die "Don't understand logon screen"
# wait ***, send next command
Espera-Envia "***" "22,1,3" "IPLINFO"
Espera-Envia "Option ===>" "3,1,11" "6"
Espera-Envia "===> " "5,1,5" "TSO LISTC"
#enough navegating, you can catch screen with snap action
sleep 800 #so you can watch the screen a little bit.
# Off to ISPF
xi Enter
xi 'CloseScript(0)'
I realize this is an old post but all the help I could google wasn't enough. I was stumped on this thing for a solid week so I am providing some sample code on how to better interact with s3270 using PHP.
#!/usr/bin/php
//This is straight from the www.php.net/proc_open page. Just sets up an array for your pipes
//This opens the process s3270 and enables the pipes for you to read and write from. We will set both to nonblocking and continually page for results later.
//This function will watch for text on the screen similar to Expect but without having to install an additional module and configure it. Basically until the text shows up on the screen it will continually re-run the ascii() command and wait indefinitely.
PHP Code:
function expecttxt ($thetxt) { global $pipes; $x = false; $g = false; while ($x == false) { sleep(1); while ($mydat = fgets($pipes[1])) { if (preg_match("/$thetxt/i", $mydat)) { $g = true; } if ($g == true) { while ($mydat = fgets($pipes[1])) { } $x = true; break 2; } } fwrite($pipes[0], "ascii()\n"); } }
//This function is the same as above except it will only wait about 10 seconds for a response and then continue running the script.
//You can then use the above functions and run commands real time. I initially did a lot of sleep(x) between commands and set it for a pretty high timeout. This resulted in an extremely inefficient script that STILL wouldn't work because occasionally network latency wouldn't get a response back before the sleep amount was up.
I wrote a library for using x3270 in Python. Here is copy-paste from code with many useful examples how to handle x3270 scripting under Python:
Code:
def get_output(self):
output = subprocess.Popen(["x3270if", "-p", self.pid, "ascii"], stdout=subprocess.PIPE).communicate()[0]
return output
def get_position(self):
return subprocess.Popen(["x3270if", "-p", self.pid, 'query("cursor")'], stdout=subprocess.PIPE).communicate()[0].strip()
def set_position(self, position):
position = position.replace(' ', ',') # so you can type position with coma or with space
subprocess.call(["x3270if", "-p", self.pid, 'MoveCursor(' + position + ')'])
def confirm(self):
subprocess.call(["x3270if", "-p", self.pid, "enter"])
def PF(self, number):
subprocess.call(["x3270if", "-p", self.pid, "PF(" + str(number) + ")"])
def type_in(self, string, position= 'None'):
x3270_position = X3270.get_position(self)
position = position.replace(',', ' ') # so you can type position with coma or with space
def x3270_write(self, string):
subprocess.call(["x3270if", "-p", self.pid, 'string(' + string + ')'])
if position == 'None':
x3270_write(self, string)
print("position is empty")
elif x3270_position == position:
print("position is equal")
x3270_write(self, string)
return True
else:
if __name__ == "__main__":
print("Position is different. I will exit so nothing gets broken.")
exit()
return False
def wait_for(self, string, timeout=50):
output = X3270.get_output(self)
counter = 0
if timeout == 'unlimited':
timeout = float("inf")
while string not in output:
sleep(0.5) #first sleep half a second for 10 seconds
counter += 1
output = X3270.get_output(self)
print(counter)
if counter >= timeout:
subprocess.call(['notify-send', '-u', 'normal', '-t', '30000', '-i', sys.path[0]+'/process-stop.png', self.win_title, 'The window does not work properly, script terminated.'])
exit()
if counter >= 30 and counter < timeout:
print("now sleeping 10 seconds")
sleep(10)
if counter >= 20 and counter < 30:
print("now sleeping 5 seconds")
sleep(5)
if counter >= 10 and counter < 20: # then sleep a second for 10 seconds
print("now sleeping half a second more")
sleep(0.5)
else: return True
Instruction how to write scripts using this library can be found in readme.
Project's site: https://github.com/mkkot/x3270
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.