Asterisk::AMI perl 实现

 use Asterisk::AMI;
        my $astman = Asterisk::AMI->new(PeerAddr        =>      '127.0.0.1',
                                        PeerPort        =>      '5038',
                                        Username        =>      'admin',
                                        Secret          =>      'supersecret'
                                );     
       
        die "Unable to connect to asterisk" unless ($astman);

        my $action = $astman->({ Action => 'Command',
                                 Command => 'sip show peers'
                                });

DESCRIPTION

^

This module provides an interface to the Asterisk Manager Interface. It's goal is to provide a flexible,powerful,and reliable way to interact with Asterisk upon which other applications may be built. It utilizes AnyEvent and therefore can integrate very easily into event-based applications,but it still provides blocking functions for us with standard scripting.

SSL SUPPORT INFORMAION

For SSL support you will also need the module that AnyEvent::Handle uses for SSL support,which is not a required dependency. Currently that module is 'Net::SSLeay' (AnyEvent:Handle version 5.251) but it may change in the future.

CentOS/Redhat

If the version of Net:SSLeay included in CentOS/Redhat does not work try installing an updated version from CPAN.

Constructor

new([ARGS])

Creates a new AMI object which takes the arguments as key-value pairs.

        Key-Value Pairs accepted:
        PeerAddr        Remote host address     <hostname>
        PeerPort        Remote host port        <service>
        Events          Enable/Disable Events           'on'|'off'
        Username        Username to access the AMI
        Secret          Secret used to connect to AMI
        AuthType        Authentication type to use for login    'plaintext'|'MD5'
        UseSSL          Enables/Disables SSL for the connection 0|1
        BufferSize      Maximum size of buffer,in number of actions
        Timeout         Default timeout of all actions in seconds
        Handlers        Hash reference of Handlers for events   { 'EVENT' => /&somesub };
        Keepalive       Interval (in seconds) to periodically send 'Ping' actions to asterisk
        TCP_Keepalive   Enables/Disables SO_KEEPALIVE option on the socket      0|1
        Blocking        Enable/Disable blocking connects        0|1
        on_connect      A subroutine to run after we connect
        on_connect_err  A subroutine to call if we have an error while connecting
        on_error        A subroutine to call when an error occurs on the socket
        on_disconnect   A subroutine to call when the remote end disconnects
        on_timeout      A subroutine to call if our Keepalive times out
        OriginateHack   Changes settings to allow Async Originates to work 0|1

        'PeerAddr' defaults to 127.0.0.1.
        'PeerPort' defaults to 5038.
        'Events' default is 'off'. May be anything that the AMI will accept as a part of the 'Events' parameter for the 
        login action.
        'Username' has no default and must be supplied.
        'Secret' has no default and must be supplied.
        'AuthType' sets the authentication type to use for login. Default is 'plaintext'.  Use 'MD5' for MD5 challenge
        authentication.
        'UseSSL' defaults to 0 (no ssl). When SSL is enabled the default PeerPort changes to 5039.
        'BufferSize' has a default of 30000. It also acts as our max actionid before we reset the counter.
        'Timeout' has a default of 0,which means no timeout on blocking.
        'Handlers' accepts a hash reference setting a callback handler for the specified event. EVENT should match
        the contents of the {'Event'} key of the event object will be. The handler should be a subroutine reference that
        will be passed the a copy of the AMI object and the event object. The 'default' keyword can be used to set
        a default event handler. If handlers are installed we do not buffer events and instead immediately dispatch them.
        If no handler is specified for an event type and a 'default' was not set the event is discarded.
        'Keepalive' only works when running with an event loop. Used with on_timeout,this can be used to detect if
        asterisk has become un-responsive.
        'TCP_Keepalive' default is disabled. Activates the tcp keep-alive at the socket layer. This does not require 
        an event-loop and is lightweight. Useful for applications that use long-lived connections to Asterisk but 
        do not run an event loop.
        'Blocking' has a default of 1 (block on connecting). A value of 0 will cause us to queue our connection
        and login for when an event loop is started. If set to non blocking we will always return a valid object.

        'on_connect' is a subroutine to call when we have successfully connected and logged into the asterisk manager.
        it will be passed our AMI object.

        'on_connect_err','on_error','on_disconnect'
        These three specify subroutines to call when errors occur. 'on_connect_err' is specifically for errors that
        occur while connecting,as well as failed logins. If 'on_connect_err' or 'on_disconnect' it is not set,but 'on_error' is,'on_error' will be called. 'on_disconnect' is not reliable,as disconnects seem to get lumped
        under 'on_error' instead. When the subroutine specified for any of theses is called the first argument is a copy
        of our AMI object,and the second is a string containing a message/reason. All three of these are 'fatal',when
        they occur we destroy our buffers and our socket connections.

        'on_timeout' is called when a keep-alive has timed out,not when a normal action has. It is non-'fatal'.
        The subroutine will be called with a copy of our AMI object and a message.

        'OriginateHack' defaults to 0 (off). This essentially enables 'call' events and says 'discard all events
        unless the user has explicitly enabled events' (prevents a memory leak). It does its best not to mess up
        anything you have already set. Without this,if you use 'Async' with an 'Originate' the action will timeout
        or never callback. You don't need this if you are already doing work with events,simply add 'call' events
        to your eventmask. 
        If you are running an event loop and use blocking methods (e.g. get_response,check_response,action,simple_action,connected) the outcome is unspecified. It may work,it may lock everything up,the action may
        work but break something else. I have tested it and behavior seems unpredictable at best and is very
        circumstantial.

        If you are running an event-loop use non-blocking callbacks! It is why they are there!

        However if you do play with blocking methods inside of your loops let me know how it goes.

Actions

Construction

No matter which method you use to send an action (send_action(),simple_action(),or action()),they all accept actions in the same format,which is a hash reference. The only exceptions to this rules are when specifying a callback and a callback timeout,which only work with send_action.

To build and send an action you can do the following:

        %action = ( Action => 'Command',Command => 'sip show peers'
                );

        $astman->send_action(/%action);

Alternatively you can also do the following to the same effect:

        $astman->send_action({  Action => 'Command',Command => 'sip show peers'
                                });

Additionally the value of the hash may be an array reference. When an array reference is used,every value in the array is append as a different line to the action. For example:

        { Variable => [ 'var1=1','var2=2' ] }

        Will become:

        Variable: var1=1
        Variable: var2=2

        When the action is sent.

Sending and Retrieving

More detailed information on these individual methods is available below

The send_action() method can be used to send an action to the AMI. It will return a positive integer,which is the ActionID of the action,on success and will return undef in the event it is unable to send the action.

After sending an action you can then get its response in one of two methods.

The method check_response() accepts an actionid and will return 1 if the action was considered successful,0 if it failed and undef if an error occurred or on timeout.

The method get_response() accepts an actionid and will return a Response object (really just a fancy hash) with the contents of the Action Response as well as any associated Events it generated. It will return undef if an error occurred or on timeout.

All responses and events are buffered,therefor you can issue several send_action()s and then retrieve/check their responses out of order without losing any information. In-fact,if you are issuing many actions in series you can get much better performance sending them all first and then retrieving them later,rather than waiting for responses immediately after issuing an action.

Alternatively you can also use simple_action() and action(). simple_action() combines send_action() and check_response(),and therefore returns 1 on success and 0 on failure,and undef on error or timeout. action() combines send_action() and get_response(),and therefore returns a Response object or undef.

Examples

        Send and retrieve and action:
        my $actionid = $astman->send_action({   Action => 'Command',Command => 'sip show peers'
                                });

        my $response = $astman->get_response($actionid)

        This is equivalent to the above:
        my $response =  $astman->action({       Action => 'Command',Command => 'sip show peers'
                                });

        The following:
        my $actionid1 = $astman->send_action({  Action => 'Command',Command => 'sip show peers'
                                });

        my $actionid2 = $astman->send_action({  Action => 'Command',Command => 'sip show peers'
                                });

        my $actionid3 = $astman->send_action({  Action => 'Command',Command => 'sip show peers'
                                });

        my $response3 = $astman->get_response($actionid3);
        my $response1 = $astman->get_response($actionid1);
        my $response2 = $astman->get_response($actionid2);

        Can be much faster than:
        my $response1 = $astman->action({       Action => 'Command',Command => 'sip show peers'
                                });
        my $response2 = $astman->action({       Action => 'Command',Command => 'sip show peers'
                                });
        my $response3 = $astman->action({       Action => 'Command',Command => 'sip show peers'
                                });

Originate Examples

        I see enough searches hit my site for this that I figure it should be included in the documentation.
        These don't include non-blocking examples,please read the section on 'Callbacks' below for information
        on using non-blocking callbacks and events.

        NOTE: Please read about the 'OriginateHack' option for the constructor above if you plan on using the 'Async'
        option in your Originate command,as it may be required to properly retrieve the response.

        In these examples we are dialing extension '12345' at a sip peer named 'peer' and when the call connects
        we drop the channel into 'some_context' at priority 1 for extension 100.

        Example 1 - A simple non-ASYNC Originate

        my $response = $astman->action({Action => 'Originate',Channel => 'SIP/peer/12345',Context => 'some_context',Exten => 100,Priority => 1});

        And the contents of respone will look similiar to the following:

        {
                'Message' => 'Originate successfully queued','ActionID' => '3','GOOD' => 1,'COMPLETED' => 1,'Response' => 'Success'
        };

        Example 2 - Originate with multiple variables
        This will set the channel variables 'var1' and 'var2' to 1 and 2,respectfully.
        The value for the 'Variable' key should be an array reference or an anonymous array in order
        to set multiple variables.

        my $response = $astman->action({Action => 'Originate',Priority => 1,Variable = [ 'var1=1','var2=2' ]});

        Example 3 - An Async Originate
        If youre Async Originate never returns please read about the 'OriginateHack' option for the constructor.

        my $response = $astman->action({Action => 'Originate',Async => 1});

        And the contents of response will look similiar to the following:

        {
                'Message' => 'Originate successfully queued','EVENTS' => [
                        {
                                'Exten' => '100','CallerID' => '<unknown>','Event' => 'OriginateResponse','Privilege' => 'call,all','Channel' => 'SIP/peer-009c5510','Context' => 'some_context','Response' => 'Success','Reason' => '4','CallerIDName' => '<unknown>','Uniqueid' => '1276543236.82','CallerIDNum' => '<unknown>'
                        }
                        ],'Response' => 'Success'
        };

        More Info:
        Check out the voip-info.org page for more information on the Originate action.
        http://www.voip-info.org/wiki/view/Asterisk+Manager+API+Action+Originate

Callbacks

        You may also specify a method to callback when using send_action as well as a timeout.

        An example of this would be:
        $astman->send_action({  Action => 'Ping',CALLBACK => /&somemethod,TIMEOUT => 7 });

        Equivalent in the new alternative sytanx:
        $astman->send_action({ Action => 'Ping' },/&somemethod,7);

In this example once the action 'Ping' finishes we will call somemethod() and pass it the a copy of our AMI object and the Response Object for the action. If TIMEOUT is not specified it will use the default set. A value of 0 means no timeout. When the timeout is reached somemethod() will be called and passed a reference to the our $astman and the uncompleted Response Object,therefore somemethod() should check the state of the object. Checking the key {'GOOD'} is usually a good indication if the response is useable.

Callback Caveats

Callbacks only work if we are processing packets,therefore you must be running an event loop. Alternatively,we run mini-event loops for our blocking calls (e.g. action(),get_action()),so in theory if you set callbacks and then issue a blocking call those callbacks should also get triggered. However this is an unsupported scenario.

Timeouts are done using timers and they are set as soon as you send the object. Therefore if you send an action with a timeout and then monkey around for a long time before getting back to your event loop (to process input) you can time out before ever even attempting to receive the response.

        A very contrived example:
        $astman->send_action({  Action => 'Ping',TIMEOUT => 3 });

        sleep(4);

        #Start loop
        $astman->loop;
        #Oh no we never even tried to get the response yet it will still time out

ActionIDs

This module handles ActionIDs internally and if you supply one in an action it will simply be ignored and overwritten.

Responses and Events

        NOTE: Empty fields sent by Asterisk (e.g. 'Account: ' with no value in an event) are represented by the hash
        value of null string,not undef. This means you need to test for ''
        (e.g. if ($response->{'Account'} ne '')) ) for any values that might be possibly be empty.

Responses

        Responses are returned as response objects,which are hash references,structured as follows:

        $response->{'Response'}         Response to our packet (Success,Failed,Error,Pong,etc).
                   {'ActionID'}         ActionID of this Response.
                   {'Message'}          Message line of the response.
                   {'EVENTS'}           Array reference containing Event Objects associated with this actionid.
                   {'PARSED'}           Hash reference of lines we could parse into key->value pairs.
                   {'CMD'}              Contains command output from 'Action: Command's. It is an array reference.
                   {'COMPLETED'}        1 if completed,0 if not (timeout)
                   {'GOOD'}             1 if good,0 if bad. Good means no errors and COMPLETED.

Events

        Events are turned into event objects,these are similar to response objects,but their keys vary much more
        depending on the specific event.

        Some common contents are:

        $event->{'Event'}               The type of Event
                {'ActionID'}            Only available if this event was caused by an action

Event Handlers

        Here is a very simple example of how to use event handlers. Please note that the key for the event handler
        is matched against the event type that asterisk sends. For example if asterisk sends 'Event: Hangup' you use a
        key of 'Hangup' to match it. This works for any event type that asterisk sends.

        my $astman = Asterisk::AMI->new(PeerAddr        =>      '127.0.0.1',PeerPort        =>      '5038',Username        =>      'admin',Secret          =>      'supersecret',Events          =>      'on',Handlers        => { default => /&do_event,Hangup => /&do_hangup };
                                );

        die "Unable to connect to asterisk" unless ($astman);

        sub do_event {
                my ($asterisk,$event) = @_;

                print 'Yeah! Event Type: ' . $event->{'Event'} . "/r/n";
        }

        sub do_hangup {
                my ($asterisk,$event) = @_;
                print 'Channel ' . $event->{'Channel'} . ' Hungup because ' . $event->{'Cause-txt'} . "/r/n";
        }

        #Start some event loop
        someloop;

How to use in an event-based application

        Getting this module to work with your event based application is really easy so long as you are running an
        event-loop that is supported by AnyEvent. Below is a simple example of how to use this module with your
        preferred event loop. We will use EV as our event loop in this example. I use subroutine references in this
        example,but you could use anonymous subroutines if you want to.

        #Use your preferred loop before our module so that AnyEvent will auto-detect it
        use EV;
        use Asterisk::AMI:

        #Create your connection
        my $astman = Asterisk::AMI->new(PeerAddr        =>      '127.0.0.1',Handlers        =>      { default => /&eventhandler }
                                );
        #Alternatively you can set Blocking => 0,and set an on_error sub to catch connection errors
        die "Unable to connect to asterisk" unless ($astman);

        #Define the subroutines for events
        sub eventhandler { my ($ami,$event) = @_; print 'Got Event: ',$event->{'Event'},"/r/n"; }

        #Define a subroutine for your action callback
        sub actioncb { my ($ami,$response) = @_; print 'Got Action Reponse: ',$response->{'Response'},"/r/n"; }

        #Send an action
        my $action = $astman->({ Action => 'Ping',CALLBACK => /&actioncb });

        #Do all of you other eventy stuff here,or before all this stuff,whichever
        #..............

        #Start our loop
        EV::loop



        That's it,the EV loop will allow us to process input from asterisk. Once the action completes it will 
        call the callback,and any events will be dispatched to eventhandler(). As you can see it is fairly
        straight-forward. Most of the work will be in creating subroutines to be called for various events and 
        actions that you plan to use.

Methods

send_action ( ACTION,[ [ CALLBACK ],[ TIMEOUT ] ] )

        Sends the action to asterisk,where ACTION is a hash reference. If no errors occurred while sending it returns
        the ActionID for the action,which is a positive integer above 0. If it encounters an error it will return undef.
        You may specify a callback function and timeout either in the ACTION hash or in the method call. CALLBACK is
        optional and should be a subroutine reference or any anonymous subroutine. TIMEOUT is optional and only has an
        affect if a CALLBACK is specified. CALLBACKs and TIMEOUTs specified during a method call override any found in
        the ACTION hash.

check_response( [ ACTIONID ],[ TIMEOUT ] )

        Returns 1 if the action was considered successful,0 if it failed,or undef on timeout or error. If no ACTIONID
        is specified the ACTIONID of the last action sent will be used. If no TIMEOUT is given it blocks,reading in
        packets until the action completes. This will remove a response from the buffer.

get_response ( [ ACTIONID ],[ TIMEOUT ] )

        Returns the response object for the action. Returns undef on error or timeout.
        If no ACTIONID is specified the ACTIONID of the last action sent will be used. If no TIMEOUT is given it 
        blocks,reading in packets until the action completes. This will remove the response from the buffer.

action ( ACTION [,TIMEOUT ] )

        Sends the action and returns the response object for the action. Returns undef on error or timeout.
        If no ACTIONID is specified the ACTIONID of the last action sent will be used.
        If no TIMEOUT is given it blocks,reading in packets until the action completes. This will remove the
        response from the buffer.

simple_action ( ACTION [,TIMEOUT ] )

        Sends the action and returns 1 if the action was considered successful,or undef on error
        and timeout. If no ACTIONID is specified the ACTIONID of the last action sent will be used. If no TIMEOUT is
        given it blocks,reading in packets until the action completes. This will remove the response from the buffer.

disconnect ()

        Logoff and disconnects from the AMI. Returns 1 on success and 0 if any errors were encountered.

get_event ( [ TIMEOUT ] )

        This returns the first event object in the buffer,or if no events are in the buffer it reads in packets
        waiting for an event. It will return undef if an error occurs.
        If no TIMEOUT is given it blocks,reading in packets until an event arrives.

amiver ()

        Returns the version of the Asterisk Manager Interface we are connected to. Undef until the connection is made
        (important if you have Blocking => 0).

connected ( [ TIMEOUT ] )

        This checks the connection to the AMI to ensure it is still functional. It checks at the socket layer and
        also sends a 'PING' to the AMI to ensure it is still responding. If no TIMEOUT is given this will block
        waiting for a response.

        Returns 1 if the connection is good,0 if it is not.

error ()

        Returns 1 if there are currently errors on the socket,0 if everything is ok.

destroy ( [ FATAL ] )

        Destroys the contents of all buffers and removes any current callbacks that are set. If FATAL is true
        it will also destroy our IO handle and its associated watcher. Mostly used internally. Useful if you want to
        ensure that our IO handle watcher gets removed. 

loop ()

        Starts an eventloop via AnyEvent.

See Also

^

Asterisk::AMI::Common,Asterisk::AMI::Common::Dev

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1. 如何去重 #!/usr/bin/perl use strict; my %hash; while(&lt;&gt;){ chomp; print &quot;$_n&quot; unless
最近写了一个perl脚本,实现的功能是将表格中其中两列的数据进行拼凑,然后将拼凑后的数据用“|”连接在一起。表格内容如下: 员工号码员工姓名职位入职日期1001张三销售1980/12/17 0:00:
表的数据字典格式如下:如果手动写MySQL建表语句,确认麻烦,还不能保证书写一定正确。写了个Perl脚本,可快速构造MySQL脚本语句。脚本如下:#!/usr/bin/perluse strict;m
巡检类工作经常会出具日报,最近在原有日报的基础上又新增了一个表的数据量统计日报,主要是针对数据库中使用较频繁,数据量又较大的31张表。该日报有两个sheet组成,第一个sheet是数据填写,第二个sh
在实际生产环境中,常常需要从后台日志中截取报文,报文的形式类似于.........一个后台日志有多个报文,每个报文可由操作流水唯一确定。以前用AWK写过一个,程序如下:beginline=`awk &
最近写的一个perl程序,通过关键词匹配统计其出现的频率,让人领略到perl正则表达式的强大,程序如下:#!/usr/bin/perluse strict;my (%hash,%hash1,@arra
忍不住在 PerlChina 邮件列表中盘点了一下 Perl 里的 Web 应用框架(巧的是 PerlBuzz 最近也有一篇相关的讨论帖),于是乎,决定在我自己的 blog 上也贴一下 :) 原生 CGI/FastCGI 的 web app 对于较小的应用非常合适,但稍复杂一些就有些痛苦,但运行效率是最高的 ;) 如果是自己用 Perl 开发高性能的站,多推荐之。 Catalyst, CGI::A
bless有两个参数:对象的引用、类的名称。 类的名称是一个字符串,代表了类的类型信息,这是理解bless的关键。 所谓bless就是把 类型信息 赋予 实例变量。 程序包括5个文件: person.pm :实现了person类 dog.pm :实现了dog类 bless.pl : 正确的使用bless bless.wrong.pl : 错误的使用bless bless.cc : 使用C++语言实
gb2312转Utf的方法: use Encode; my $str = "中文"; $str_cnsoftware = encode("utf-8", decode("gb2312", $str));   Utf转 gb2312的方法: use Encode; my $str = "utf8中文"; $str_cnsoftware = encode("gb2312", decode("utf-8
  perl 计算硬盘利用率, 以%来查看硬盘资源是否存在IO消耗cpu资源情况; 部份代码参考了iostat源码;     #!/usr/bin/perl use Time::HiRes qw(gettimeofday); use POSIX; $SLEEPTIME=3; sub getDiskUtl() { $clock_ticks = POSIX::sysconf( &POSIX::_SC_
1 简单变量 Perl 的 Hello World 是怎么写的呢?请看下面的程序: #!/usr/bin/perl print "Hello World" 这个程序和前面 BASH 的 Hello World 程序几乎相同,只是第一行换成了 #!/usr/bin/perl ,还有显示的时候用的是 print,而不是 echo。有了前面 BASH 基础和 C 语言的基础,许多 Perl 的知识可以很
本文介绍Perl的Perl的简单语法,包括基本输入输出、分支循环控制结构、函数、常用系统调用和文件操作,以及进程管理几部分。 1 基本输入输出 在 BASH 脚本程序中,我们用 read var 来实现从键盘的输入,用 echo $var 来实现输出。那么在 Perl 中将有一点变化。Perl 中将标准输入用关键词 表示;标准输出用 表示,标准错误输出用 表示。故而从标准输入读取数据可以写成: $
正则表达式是 Perl 语言的一大特色,也是 Perl 程序中的一点难点,不过如果大家能够很好的掌握他,就可以轻易地用正则表达式来完成字符串处理的任务,当然在 CGI 程序设计中就更能得心应手了。下面我们列出一些正则表达式书写时的一些基本语法规则。 1 正则表达式的三种形式 首先我们应该知道 Perl 程序中,正则表达式有三种存在形式,他们分别是: 匹配:m/<regexp>/ (还可以简写为 /
在学习Perl和Shell时,有很多人可能会问这样一个问题,到底先学习哪个或者学习哪个更好! 每个人都有自己的想法,以下是个人愚见,请多多指教! Perl是larry wall为解决日常工作中的一个编程问题而产生的,它最初的主要功能是用于分析基于文本的数据和生成这些数据的统计和结果;尽管初衷很简单,但是后来发展了很多特点: 1、Perl是一种借鉴了awk、C、sed、shell、C++、Java等
Perl 有很多命令行参数. 通过它, 我们有机会写出更简单的程序. 在这篇文章里我们来了解一些常用的参数. (重点提示:在window下执行命令行程序的方式为 : perl -e "some code", 注意:是双引号啊,不是单引号,linux下执行时单引号) Safety Net Options 在使用 Perl 尝试一些聪明( 或 stupid) 的想法时, 错误难免会发生. 有经验的 P
转自: http://bbs.chinaunix.net/thread-1191868-1-1.html# 让你的perl代码看起来更像perl代码,而不是像C或者BASIC代码,最好的办法就是去了解perl的内置变量。perl可以通过这些内置变量可以控制程序运行时的诸多方面。 本文中,我们一起领略一下众多内置变量在文件的输入输出控制上的出色表现。 行计数 我决定写这篇文章的一个原因就是,当我发现
2009-02-02 13:07 #!/usr/bin/perl # D.O.M TEAM - 2007 # anonyph; arp; ka0x; xarnuz # 2005 - 2007 # BackConnectShell + Rootlab t00l # priv8! # 3sk0rbut0@gmail.com # # Backconnect by data cha0s (modifica
转自: http://bbs.chinaunix.net/thread-1191868-1-1.html# 让你的perl代码看起来更像perl代码,而不是像C或者BASIC代码,最好的办法就是去了解perl的内置变量。perl可以通过这些内置变量可以控制程序运行时的诸多方面。 本文中,我们一起领略一下众多内置变量在文件的输入输出控制上的出色表现。 行计数 我决定写这篇文章的一个原因就是,当我发现
黑莓 手机 屏幕发展历程对比 blackberry 各型号屏幕大小   黑莓手 机 一直在不断发展且新机型 也在不断上市. 因此,不同黑莓机型的屏幕分辨率也在不断变化着. 总的来说,屏幕分辨率一直在提高并且越来越清晰.我们对所有的黑莓 机型的屏幕分辨率做了个对比.~51blackberry ~com     可能大家特别感兴趣是新发布的黑莓机型,它的分辨率也是黑莓 机型中前所未有的.   黑莓 b
      公司里没有我用惯的UltraEdit的lisence了, 只能无奈转向开源的Notepad++, 找了半天才知道配置运行Perl的办法。         1,用Notepad++打开.pl文件,         2, F5或者Run->Run,打开运行窗口,在下面的框框里输入:Perl -w "$(FULL_CURRENT_PATH)", 然后Save,保存成一个命令就行,名字比如叫R