设为首页 收藏本站
查看: 1944|回复: 0

[Cloudstack] kettle 4.1 源码分析

[复制链接]

尚未签到

发表于 2015-10-14 08:55:33 | 显示全部楼层 |阅读模式
kettle4.1源码研究
分类: java2012-06-2713:40 4934人阅读 评论(3) 收藏 举报eclipsestringimageswtconstructoreclipse插件
目录(?)[+]

  Kettle源码分析
一.获取并编译源码
1.1.           获取源码
Svn获取
  svn://source.pentaho.org/svnkettleroot/Kettle/tags
  
网页获取
  官方网站:http://kettle.pentaho.com/
1.2.           编译源码
将项目加载到eclipse
  kettle项目拷贝到eclipseworkspace目录下,在eclipse中新建javaproject,项目名称和你拷贝过来的kettle文件夹名称一致
  
  
  项目导入到eclipse中会出现一个错误,如下图,将这个文件的源码全部注释掉
  
  
  
编译
  打开build.xml, 在右边的。Outline 点击kettle->run  as ->ant build
  
  
  第一次编译的时候需要从网上下载几个文件,放在C:\Documents and Settings\Administrator\.subfloor,网络不好的话下载会比较慢,也可以直接文件放在C:\Documents and Settings\Administrator\下。编译完成后将bin目录下的.bat文件拷贝到Kettle目录下点击Spoon.bat运行,运行成功代表编译已近通过
用源码运行Spoon
  Kettle源码工程本身可能是在linux64位机器上调试的,swt配置是linux64的库,所有在运行源码前需要修改成win32swt,步骤如下:工程à属性àJavaBuild Pathàlibrariesàadd jars
  
  然后将linux64SWT库删除
  
  最后打开src-uiàorg.pentaho.di.ui.spoonàSpoon.java  Run As àjavaapplication
二.源码分析
2.1.           修改kettle界面
修改初始化界面
  打开package org.pentaho.di.ui.spoonSpoon.Java,找到main函数,该main函数为Spoon工具的入口,找到如下语句
  
  Splash splash = new Splash(display);
         该语句为spoon初始化显示的界面,跳到定义Splash.java,下面函数

canvas.addPaintListener(new PaintListener() {

      publicvoid paintControl(PaintEvent e) {

        String versionText = BaseMessages.getString(PKG, "SplashDialog.Version") + " " + Const.VERSION; //$NON-NLS-1$//$NON-NLS-2$

      

        StringBuilder sb = new StringBuilder();

        String line = null;

      

        try {

          BufferedReader reader = new BufferedReader(newInputStreamReader(Splash.class.getClassLoader().getResourceAsStream("org/pentaho/di/ui/core/dialog/license/license.txt")));//$NON-NLS-1$

         

          while((line = reader.readLine()) != null) {

            sb.append(line + System.getProperty("line.separator")); //$NON-NLS-1$

          }

        } catch (Exception ex) {

          sb.append(""); //$NON-NLS-1$

          Log.warn(BaseMessages.getString(PKG, "SplashDialog.LicenseTextNotFound")); //$NON-NLS-1$

        }

      

        String licenseText = sb.toString();

        e.gc.drawImage(kettle_image, 0, 0);


        // If this is a Milestone or RC release, warn the user

        if (Const.RELEASE.equals(Const.ReleaseType.MILESTONE)) {

        versionText = BaseMessages.getString(PKG, "SplashDialog.DeveloperRelease") + " - " + versionText; //$NON-NLS-1$//$NON-NLS-2$

          drawVersionWarning(e);

        } elseif (Const.RELEASE.equals(Const.ReleaseType.RELEASE_CANDIDATE)){

          versionText = BaseMessages.getString(PKG, "SplashDialog.ReleaseCandidate") + " - " + versionText;  //$NON-NLS-1$//$NON-NLS-2$

        }

        elseif (Const.RELEASE.equals(Const.ReleaseType.PREVIEW)){

          versionText = BaseMessages.getString(PKG, "SplashDialog.PreviewRelease") + " - " + versionText;  //$NON-NLS-1$//$NON-NLS-2$

        }

        elseif (Const.RELEASE.equals(Const.ReleaseType.GA)){

            versionText = BaseMessages.getString(PKG, "SplashDialog.GA") + " - " + versionText;  //$NON-NLS-1$//$NON-NLS-2$

          }


        Font verFont = new Font(e.display, "Helvetica", 11, SWT.BOLD); //$NON-NLS-1$

        e.gc.setFont(verFont);

        e.gc.drawText(versionText, 290, 205, true);

               

        // try using the desired font size for the license text

        int fontSize = 8;

        Font licFont = new Font(e.display, "Helvetica", fontSize, SWT.NORMAL); //$NON-NLS-1$

        e.gc.setFont(licFont);


        // if the text will not fit the allowed space

        while (!willLicenseTextFit(licenseText, e.gc)) {

          fontSize--;

          licFont = new Font(e.display, "Helvetica", fontSize, SWT.NORMAL); //$NON-NLS-1$

          e.gc.setFont(licFont);         

        }

      

        e.gc.drawText(licenseText, 290, 290, true);

      }

    });



1.       修改背景图片

找到ui/image/下面的kettle_splash.png,替换该图片

2.       修改版本信息

找到e.gc.drawText(versionText, 290, 205, true); 改为e.gc.drawText("海康威视数据交换平台V1.0",290, 205, true);

3.       修改下面的描述性文字

找到e.gc.drawText(licenseText, 290, 290, true);改为e.gc.drawText("作者:海康",290, 290, true);

4.       预览效果


修改spoon主界面标题
          
           找到spoon.java下面的
  public static final String APP_NAME = BaseMessages.getString(PKG, "Spoon.Application.Name");
  修改为要改成的标题
修改spoon界面主菜单栏
  Ui/spoon.xul下引用menubar.xul代码,
  
  <pen:include src=&quot;menubar.xul&quot; ignoreroot=&quot;true&quot;/>
  menubar.xul为主菜单栏的配置处
修改spoon菜单栏
  Spoon菜单栏
  
  配置的地方在spoon.xul下的
<menupopup id=&quot;new-file-popup&quot;>

    <menuitem label=&quot;${Spoon.Menubar.File.NewJob}&quot; command=&quot;spoon.newJobFile()&quot; image=&quot;${ChefIcon_image}&quot;/>

<menuitem id=&quot;menubar-new-trans&quot; label=&quot;${Spoon.Menubar.File.NewTrans}&quot; command=&quot;spoon.newTransFile()&quot; image=&quot;${SpoonIcon_image}&quot;/>

  <menuseparator />

    <menuitem label=&quot;${Spoon.Menubar.File.NewDatabaseConn}&quot; command=&quot;spoon.newConnection()&quot; image=&quot;${CNC_image}&quot;/>

    <menuitem label=&quot;${Spoon.Menubar.File.NewSlave}&quot; command=&quot;spoon.newSlaveServer()&quot; image=&quot;${Slave_image}&quot;/>

  </menupopup>

修改转换和job插件列表及图标
  Src/kettle-steps.xml是旁边转换插件列表的配置文件处
  文件分析如下,这个为表输入的配置
<step id=&quot;TableInput&quot;>

<description>i18n:org.pentaho.di.trans.step:BaseStep.TypeLongDesc.TableInput</description>

      <classname>org.pentaho.di.trans.steps.tableinput.TableInputMeta</classname>

      <category>i18n:org.pentaho.di.trans.step:BaseStep.Category.Input</category>

      <tooltip>i18n:org.pentaho.di.trans.step:BaseStep.TypeTooltipDesc.TableInput</tooltip>

      <iconfile>ui/images/TIP.png</iconfile>

  </step>
  classname为对应的源文件, category为属于哪个分类下,例如tableinput属于“输入”组,tooltip提示信息,iconfile显示图片
主面板布局
  spoon.java下的private void addTree()方法内
  
2.2.           Kettle数据转换流程
  Kettle数据转换流程是一个生产者消费者模型,每个步骤为一个节点,每个节点是一个单独的线程,节点和节点之间通过一个阻塞队列传递数据,前一个节点一条一条的往阻塞队列写入,后面节点一条一条的从阻塞队列读取,阻塞队列实现方法在src-core/org/pentaho/di/core/BlockingRowSet.java
  

表输入流程
  表输入通过JDBC连接数据库,执行SQL语句后返回一个ResultSet(结果集),如果数据库支持PreparedStatement. setFetchSize(每次返回的多少条记录到结果集),分批将数据返回到结果集中,然后逐条从结果集中读取数据写入到阻塞队列中;如果不支持分批次读取,则一次行将数据返回到ResultSet中,然后逐条读取写入阻塞队列,数据库的操作的方法在src-db/org/pentaho/di/core/database/Database.java中实现。
  
插入更新流程
  
2.3.           插件研究
kettle转换步骤工作组件
       这里有四个类构成了这个kettle 步骤/节点,每一个类都有其特定的目的及所扮演的角色。

TemplateStep:  步骤类实现了StepInteface接口,在转换运行时,它的实例将是数据实际处理的位置。每一个执行线程都表示一个此类的实例。


TemplateStepData:  数据类用来存储数据,当插件执行时,对于每个执行的线程都是唯一的。执行时里面存储的东西主要包括数据库连接、文件句柄、缓存等等其他东西。


TemplateStepMeta: 元数据类实现了StepMetaInterface接口。它的职责是保存和序列化特定步骤实例的配置,在我们这个例子中,它负责保存用户设置的步骤名称和输出字段的名称。


TemplateStepDialog:对话框类实现了该步骤与用户交互的界面,它显示一对话框,通过对话框用户可以自己的喜好设定步骤的操作。对话框类与元数据类关系非常紧密,元数据类可以追踪用户的设置。


除了上面的代码,还有一个plugin.xml,它设置好了插件的元数据,定义了步骤在kettle图形工作台中的显示效果。为了更好的让大家理解,我将利用这个步骤设计一个转换流程并执行它。对于插件的开发,我们将从plugin.xml配置文件开始讲起,然后讲讲元数据和对话框类,最后再讲讲步骤类和数据类。


书写你自己的plugin.xml
       下面plugin.xml是我们这个插件里面的内容,它的功能是告诉kettle插件的元数据类,插件的名称及描叙,还有需要加载的jar包。想要了解细节,可以查看文章:plug-inloading

<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?>

<plugin

   id=&quot;TemplatePlugin&quot;

   iconfile=&quot;icon.png&quot;

   description=&quot;Template Plugin&quot;

   tooltip=&quot;Only there for demonstration purposes&quot;

   category=&quot;Demonstration&quot;

   classname=&quot;plugin.template.TemplateStepMeta&quot;>

   <libraries>

      <library name=&quot;templatestep.jar&quot;/>

   </libraries>

</plugin>


ID:在kettle插件中必须全局唯一,因为被kettle序列化了,所以不要随便改变

Iconfile: kettle中插件显示的图片,必须是png图片

Description:插件描叙,显示在树形菜单里面。

Tooltip:树形菜单中,鼠标滑过的时候显示的提示信息

Category:插件显示的父目录

Classname:元数据类

Library:指明了插件需要加载所依赖的jar



插件主要包含类介绍
一、元数据类:

         下面显示了元数据的几个关键的方法,注意元数据类里面用私有成员变量outputField 存储了下一个步骤的输出字段。

// keep track of the step settings
public String getOutputField()
public void setOutputField(…)
public void setDefault()

// serialize the step settings to and from xml
public String getXML()
public void loadXML(…)

// serialize the step settings to and from a kettle repository
public void readRep(…)
public void saveRep(…)

// provide information about how the step affects the field structure of processed rows
public void getFields(…)

// perform extended validation checks for the step
public void check(…)

// provide instances of the step, data and dialog classes to Kettle
public StepInterface getStep(…)
public StepDataInterface getStepData()
public StepDialogInterface getDialog(…)

TemplateStepMeta元数据类其实还有很多方面,不过大多被他的父类BaseStepMeta给默认实现了,这些默认的实现足以使我们的元数据类工作良好。想要了解更多,大家可以查查关于StepMetaInteface和BaseStepMeta的kettle官方文档。


二、对话框类:

         TemeplateStepDialog为步骤实现了对话框的设置,kettle的用户界面部件是使用的eclipse的swt框架,如果要开发比较复杂的对话框,你还必须熟悉大部分swt代码。 Swt文档大家可以从eclipse上的帮助菜单点击在线获取。在开发过程中,一个对话框对象拥有一个元数据对象,它记录了应该从哪里读取配置?应该把设置好的配置保存在哪里?它仅仅设置了输出字段的名称在我们这个模板步骤里面。一个继承自BaseStepDialog特定的对话框类必须提供open(…)方法,这个方法必须返回这个步骤的名称(发生改变时)或NULL(对话框被取消时)



三、步骤类:

         步骤类是实际的处理和转换工作的地方。因为大部分样本代码已经由父类BaseStep提供了,大多数插件仅仅关注下面几个特定的方法就行。

// initialization and teardown
public boolean
init(…)
public void
dispose(..)

// processing rows
public void
run()
public boolean
processRow(..)

Init()方法在转换执行前被kettle调用,转换必须在所有步骤初始化成功时才真正执行。我们这个模板步骤没有做任何事情,这里仅仅是拿出来让大家了解了解。

dispose()方法是在步骤执行完之后执行(非转换执行完哈),它完成资源的关闭,像文件句柄、缓存等等。

run()方法在实际处理记录集的时候调用。里面其实是个调用processRow()方法处理记录的小循环,当此步骤再没有数据处理或转换被停止时退出循环。

processRow()方法在处理单条记录的时候被调用。这个方法通常通过调用getRow()来获取需要处理的单条记录。 这个方法如果有需要将会被阻塞,例如当此步骤希望放慢脚步处理数据时。processRow()随后的流程将执行转换工作并调用putRow()方法将处理过的记录放到它的下游步骤。

注意:你的步骤可能会变记录的结构,为了安全起见,一定要多熟悉包org.pentaho.di.core.row,特别是类RowMetaInterface和RowDataUtil。

  基类BaseStep对处理的记录提供了第一次访问的标识,在某些代码只执行一次的时候可能非常有用,例如某个费时的查找,其实这就是缓存。


四、数据类:

         大多数步骤都需要临时的缓冲或者临时的数据。数据类就是这些数据合适的存放位置。每一个执行线程将得到其拥有的数据类实例,所以它能在独立的空间里面运行。TemplateStepData继承自BaseStepData,作为一个经验法则,不要将non-constant字段放置BaseStepData类里面,如果你必须,请将它最好放置TemplateStepData数据类里面.


我们的步骤仅仅使用了一个数据对象来存储记录集输出的结构,没有用到其他的存储介质,例如文件等等。

开发插件实例
  1.       kettle-steps.xml下添加如下节点
  
  
<step id=&quot;MyTest&quot;>

       <description>MyTest</description>

       <classname>mytest.MyTestMeta</classname>

       <category>插件测试</category>

       <tooltip>测试</tooltip>

       <iconfile>ui/images/TIP.png</iconfile>

  </step>
  









  2.       创建插件类
  
  MyTest类代码
  
package mytest;


import org.pentaho.di.trans.Trans;

import org.pentaho.di.trans.TransMeta;

import org.pentaho.di.trans.step.BaseStep;

import org.pentaho.di.trans.step.StepDataInterface;

import org.pentaho.di.trans.step.StepInterface;

import org.pentaho.di.trans.step.StepMeta;



publicclass MyTest extends BaseStep implements StepInterface{


public MyTest(StepMeta stepMeta, StepDataInterface stepDataInterface,

        int copyNr, TransMeta transMeta, Trans trans) {

    super(stepMeta, stepDataInterface, copyNr, transMeta, trans);

    // TODO Auto-generated constructor stub

}


}

  

  MyTestData类代码
  
  
package mytest;


import org.pentaho.di.trans.step.BaseStepData;

import org.pentaho.di.trans.step.StepDataInterface;


publicclass MyTestData extends BaseStepData implements StepDataInterface {


}

  
  MyTestMeta类代码
  
package mytest;


import java.util.List;

import java.util.Map;


import org.pentaho.di.core.CheckResultInterface;

import org.pentaho.di.core.Counter;

import org.pentaho.di.core.database.DatabaseMeta;

import org.pentaho.di.core.exception.KettleException;

import org.pentaho.di.core.exception.KettleXMLException;

import org.pentaho.di.core.row.RowMetaInterface;

import org.pentaho.di.repository.ObjectId;

import org.pentaho.di.repository.Repository;

import org.pentaho.di.trans.Trans;

import org.pentaho.di.trans.TransMeta;

import org.pentaho.di.trans.step.BaseStepMeta;

import org.pentaho.di.trans.step.StepDataInterface;

import org.pentaho.di.trans.step.StepInterface;

import org.pentaho.di.trans.step.StepMeta;

import org.pentaho.di.trans.step.StepMetaInterface;

import org.w3c.dom.Node;


publicclass MyTestMeta extends BaseStepMeta implements StepMetaInterface{


    public MyTestMeta()

    {

       super();

    }

    @Override

    publicvoid setDefault() {

       // TODO Auto-generated method stub


    }


    @Override

    publicvoid loadXML(Node stepnode, List<DatabaseMeta> databases,

           Map<String, Counter> counters) throws KettleXMLException {

       // TODO Auto-generated method stub


    }


    @Override

    publicvoid saveRep(Repository rep, ObjectId id_transformation,

           ObjectId id_step) throws KettleException {

       // TODO Auto-generated method stub


    }


    @Override

    publicvoid readRep(Repository rep, ObjectId id_step,

           List<DatabaseMeta> databases, Map<String, Counter> counters)

           throws KettleException {

       // TODO Auto-generated method stub


    }


    @Override

    publicvoid check(List<CheckResultInterface> remarks, TransMeta transMeta,

           StepMeta stepMeta, RowMetaInterface prev, String[] input,

           String[] output, RowMetaInterface info) {

       // TODO Auto-generated method stub


    }


    @Override

    public StepInterface getStep(StepMeta stepMeta,

           StepDataInterface stepDataInterface, int copyNr,

           TransMeta transMeta, Trans trans) {

       // TODO Auto-generated method stub

       returnnull;

    }


    @Override

    public StepDataInterface getStepData() {

       // TODO Auto-generated method stub

       returnnull;

    }


}

  
  
  
  MyTestDialog类代码
  
package mytest;


import org.eclipse.swt.SWT;

import org.eclipse.swt.widgets.Display;

import org.eclipse.swt.widgets.Shell;

import org.pentaho.di.trans.TransMeta;

import org.pentaho.di.trans.step.BaseStepMeta;

import org.pentaho.di.trans.step.StepDialogInterface;

import org.pentaho.di.ui.trans.step.BaseStepDialog;


publicclass MyTestDialog extends BaseStepDialog implements StepDialogInterface{

   

    public MyTestDialog(Shell parent, Object in,

           TransMeta transMeta, String stepname) {

       super(parent, (BaseStepMeta)in, transMeta, stepname);

       // TODO Auto-generated constructor stub

    }


    @Override

    public String open() {

       Shell parent = getParent();

       Display display = parent.getDisplay();


       shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE |SWT.MAX | SWT.MIN);

       props.setLook(shell);

       shell.open();

       shell.setSize(200, 200);

       shell.setText(&quot;hello&quot;);

       returnnull;

    }


}

  
  
  TestDialog类外的3个类的代码都是在加入继承基类和接口后更加eclipse插件提示自动生成的,也是我们自己需要实现的方法,TestDialog类中的MyTestDialog方法参数是固定的,根据生成的需要修改下,open函数是要我们自己实现的,运行效果如下
  
  双击MyTest后弹出一个空白的窗口
  
调用use define java class 插件
  kettle中自带的samples\transformations\User Defined Java Class - Calculate the date of Easter.ktr为例
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException

{

  Object[] r=getRow();//从阻塞队列中获取一个数据对象(一行数据记录)

  if (r==null)//如果没有可获取的数据,代表以处理完成

  {

    setOutputDone();//设置处理完成标志

    return false;//退出循环

  }


  if (first) {//第一次进入循环

    //初始化动作

     first=false;//设置为非第一次循环

  }

/*

*处理函数

*/

  logBasic(r[0].toString());//打印

  putRow(data.outputRowMeta, r);//输出到阻塞队列

  return true;

  }
  use define java class 插件其实就是一个空插件,然后我们自己来实现processRow函数体,kettle通过while循环来调用processRow函数一行一行的处理数据,流程如下所示
  
更多0

  • 上一篇双机热备中解决IP漂移后arp表不更新
  • 下一篇bho开发地址
主题推荐源码stringbuilderinitialization数据库application猜你在找kettle api 执行转换kettle(PDI)中spoon采用JNDI方式的Database Connection测试/运行配置方法kettle 转换控件翻译【Kettle从零开始】第五弹之Kettle转换中常用组件介绍wcf webhttpbinding在Eclipse中利用XMLBuddy开发XML【杂谈】Solr的自动聚类carrot2和facet关系和比较Spring整合ActiveMQSQL临时表ireport table 合并查看评论2楼 天山来客 2013-03-28 17:17发表 [回复] DSC0000.jpg 这个很强大...4.2的..1楼 wychytu 2012-11-12 19:44发表 [回复] DSC0001.jpg 太牛了,正是我想要的。话说最上面的图没贴出来,楼主有空补上吧。Re: qq250941970 2012-11-22 16:05发表 [回复] DSC0002.jpg 回复wychytu:谢谢,资源文档里面有,可以去下载资源文档
http://download.iyunv.com/detail/qq250941970/4481525发表评论

  • 用 户 名:
  • rjcs888


  • 评论内容:
  • DSC0003.gif

      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场 DSC0004.png 核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker VPN ERPIE10 Eclipse CRM JavaScript Ubuntu NFC WAP jQuery 数据库 BI HTML5 SpringApache .NET API HTML SDK IIS Fedora XML LBS Unity Splashtop UMLcomponents WindowsMobile Rails QEMU KDE Cassandra CloudStack FTC coremailOPhone CouchBase 云计算 iOS6 Rackspace WebApp SpringSide Maemo Compuware大数据 aptech Perl Tornado Ruby Hibernate ThinkPHP Spark HBase Pure SolrAngular CloudFoundry Redis Scala Django Bootstrap
    个人资料

    DSC0005.jpg
    qq250941970

    • 访问:55112次
    • 积分:958分
    • 排名:第16821名


    • 原创:36篇
    • 转载:23篇
    • 译文:0篇
    • 评论:20条


    文章搜索



    文章分类


  • C(8)
  • C&#43;&#43;(7)
  • eucalyptus(2)
  • libvirt API学习(8)
  • linux(11)
  • linux图形化编程(2)
  • python(2)
  • xen(4)
  • java(1)
  • 网络(8)
  • 资料(1)
  • 软件工程(1)
  • linux内核(1)
  • java(0)
  • zenoss(1)

    文章存档


  • 2013年02月(1)
  • 2013年01月(1)
  • 2012年06月(1)
  • 2012年03月(3)
  • 2012年02月(2)展开

    阅读排行


  • kettle4.1源码研究(4929)
  • linux设置多个IP(4584)
  • Libevent参考手册:evbuffer:缓冲IO实用功能(3190)
  • LibvirtXML学习笔记(2955)
  • libvirtAPI学习笔记(2922)
  • xen基础(1868)
  • 批量替换文本内容linux sed命令(1702)
  • XEN下的网络的启动过程(1533)
  • UDP通信绑定指定IP(1441)
  • 实现linux下上下键和命令补全(1372)

    评论排行


  • 用XML文件创建虚拟机文件(5)
  • Linux批量替换文件内容(4)
  • kettle4.1源码研究(3)
  • LibvirtXML学习笔记(2)
  • libvirtAPI学习笔记(2)
  • AXIS2C启动虚拟机示例(1)
  • XEN下的网络的启动过程(1)
  • linux伪终端telnet源码(1)
  • 基于libvirtAPI监测xen初探(1)
  • linux内存共享mmap(0)

    推荐文章

    最新评论


  • libvirtAPI学习笔记HoneySweetHeart:@zhangguangxuang:我也有同样的问题,能分享一下解决方法么?
  • LibvirtXML学习笔记fghjkldf:同问
  • LibvirtXML学习笔记无忧无缘:请问xml文件是如何生成的呢??是不是有个模板之类的东西??
  • libvirtAPI学习笔记zhangguangxuang:你好,我在编译ex1.c文件时出现下面的错误,希望你能帮我看看。ex1.c:5:27: 错误:lbv...
  • Linux批量替换文件内容qq250941970:test 文件内容aaaa1bbbbbbbbaa aaaab2bbbbbbbbaaaaaabc3b...
  • Linux批量替换文件内容qq250941970:#!/usr/bin/perl -wopen(FILE, &quot;test&quot;)|| die (&quot;Could...
  • Linux批量替换文件内容ynzyloo1:楼主,我的文件目录有两层,有上千个文件夹,每个文件夹里面有n文件。这个方法能批量修改这些文件吗?
  • Linux批量替换文件内容qq250941970:#!/usr/bin/perl -wopen(FILE,&quot;test&quot;)|| die (&quot;Could ...
  • XEN下的网络的启动过程BonseYuan:请问你用过libvirt得virNetworkGetBridgeName(networkPtr),v...
  • kettle4.1源码研究天山来客:这个很强大...4.2的..

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-126468-1-1.html 上篇帖子: 360校园招聘2015技术类笔试题 下篇帖子: IOS中NSUserDefaults的用法(轻量级本地数据存储)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表