網頁

2015年5月6日 星期三

python decorator

只是筆記一下

目的:
假設我們想要對某些function的回傳值做一個簡單的處理:
如果回傳值昰list且指有一個元素就直接取出(不放在list裡面)

def unpack_list_for_one_item(fn):                                                                            
    "unpack list for one item, if fn return ['5.1'] than return '5.1'"                                        
    def decorator(*args, **kwargs):                                                                          
        fnRet = fn(*args, **kwargs)                                                                          
        if not isinstance(fnRet, list):                                                                      
            raise TypeError("The decorator-unpack_list_for_one_item input fn{0}'s result shall be list type".format(fn))                                                                                                      
                                                                                                             
        return fnRet[0] if 1==len(fnRet) else fnRet                                                          
    return decorator                                                                                          

用法:
    @unpack_list_for_one_item
    def major(self):      
       ....

    @unpack_list_for_one_item
    def minor(self):      
       ....

    @unpack_list_for_one_item
    def patPath(self):      
       ....

解說:
1. decorator如果沒有額外參數input和output都是function
2. decorator即使沒有被呼叫也會被run過(第一層),且在main之前就被run過,如下:
#!/usr/bin/python3

from functools import wraps, partial
import logging

def logged(func):
        print ("%r %s" % (locals(), "0"))
        def innerDecorate(*args, **kwargs):
                print ("%r %s" % (locals(), "1"))
                return func
        return innerDecorate

@logged
def add(x, y):
        return x + y


注意, add並沒有被呼叫, 但結果如下:
{'func': <function add at 0x7f6798c77170>} 0

這表示 print ("%r %s" % (locals(), "0")) 有被執行
並且注意 print ("%r %s" % (locals(), "1")) 沒有被執行

但注意我們只是對func加上decorator

那如果是有參數的decorator會如何?
#!/usr/bin/python3



from functools import wraps, partial

import logging





def logged(level, name=None, message=None):

        print ("%r %s" % (locals(), "0"))

        def decorate(func):

                logname =  name if name else func.__module__

                log = logging.getLogger(logname)

                logmsg = message if message else func.__name__

                print ("%r %s" % (locals(), "1"))



                @wraps(func)

                def wrapper(*args, **kwargs):

                        print ("%r %s" % (locals(), "2"))

                        log.log(level, logmsg)

                        return func(*args, **kwargs)



                return wrapper

        return decorate



@logged(logging.DEBUG)

def add(x, y):

        return x + y


注意,一樣add並沒有被呼叫, 但結果如下:
{'message': None, 'level': 10, 'name': None} 0
{'message': None, 'logmsg': 'add', 'func': <function add at 0x7f8c9f649710>, 'logname': '__main__', 'name': None, 'log': <logging.Logger object at 0x7f8c9f66db10>, 'level': 10} 1

這表示 print ("%r %s" % (locals(), "0")) / print ("%r %s" % (locals(), "1"))  有被執行
並且注意 print ("%r %s" % (locals(), "2")) 沒有被執行

2015年3月18日 星期三

如何在apache2設定php5

主題: 如何在apache2設定php5

環境: Ubuntu

目標: 網頁show出hello1

Commands:

1. create kkk.php
cd /var/www
mkdir cgi-bin
vim kkk.php
-----------------
<?php

echo "hello1";

?>

2.  確定有沒有libphp5.so
find / -name libphp5.so  -print

3. 如果沒有libphp5.so裝一個
apt-get install libapache2-mod-php5

4. 確定位置
find / -name libphp5.so -print
/usr/lib/apache2/modules/libphp5.so

5. 設定apache2.conf
vim /etc/apache2/apache2.conf
加入
-------------------------------------------
LoadModule php5_module  /usr/lib/apache2/modules/libphp5.so
AddType application/x-httpd-php5 .php

6. 重啟動apache2
apachectl restart

7. 正常的話
http://localhost/cgi-bin/kkk.php
會出現
hello1

2014年11月2日 星期日

[封包如何在子網域間流動 ?] subnet mask, network number, subnet mask

主題: 封包如何在子網域間流動 ?


環境:
     src:   10.13.20.127 (位於10.13.20.0 子網域下)
     dest:  10.13.18.54  (位於10.13.18.0的子網域下)
     subnet mask : 255.255.248.0


NOTE: 如果不記得 subnet設置方式的朋友可以先看 http://www.clyeh.org/ipsubnet/ipsubnet.htm 來複習一下。


主要的Policy:
    (ip & subnet mask) ===> network number
    network number必須在gateway的ip table上找到對應的interface

對就是上面兩句話就是子網域下傳送的方式


看一個範例好了

(src: 10.13.20.127, dest: 10.13.18.54) ---> gateway1

gateway1上面的routing table若是有

....
10.13.18.0  255.255.248.0  eth?
...
的record

那麼這個封包就會被傳送到 eth1,eth1一般會是10.13.18.0的gateway(gateway2)

因為10.13.18.54是gateway2下的一個node,gateway2自然就會把封包傳送到這一台node上

當然,當封包要回傳的時候就是相反的順序


換言之,gateway1和gateway2上面的routing table上要有互相的record封包發送的來回才不會出問題。

也就是 gateway2上面的routing table也要有
....
10.13.20.0  255.255.248.0  eth?
...
的record


結論: 雖然網域被切開了,但是封包的傳送只要routing table設定正確理論上都要可以正常的收發。

最後附上一張圖來表示這個概念:

NOTE: 也就是說穿就是一個將 dst ip 轉換成 interface的一個系統,當有一個dst ip,利用這樣的轉換就可以知道要送到哪個interface上,不管在 PC 、手機、switch、gateway、router上都會有一個這樣的系統,尤其一個有幾十個以上的interface的switch/gateway/router上這樣的routing table更是龐大。

NOTE: dst ip是input ,而ip mask/network number/interface則是是先先設定好的資訊(如何設定? 在linux/windows上可以查詢 'route' 這個指令)

2014年9月9日 星期二

有關FTP自動開port (hole punching)

問題:

Nessus偵測到的FTP server風險。
在同一個區網內的device皆可以借由ftp偷取ftp server上的資訊。

主要原因是因為FTP的protocol在加上router自動hole punching所造成的安全性風險。

環境:

A: ftp client (192.168.1.100)
A1: another ftp client (192.168.1.101)
B: ftp client's router
C: ftp server with public ip

流程:

A ---> B ---> C  (A送出ftp command: PORT ip:port)
C就會replay

在過程中B會因為看到 dst的 port為21 port,因此會偷改 PORT ip:port 為 PORT external_ip:random_port

因此在ftp server端是分辨不出來是否為同一個來源,只知道是在B之下的一個client。

因此 A1利用wireshark或是其他的軟體可以偽造一個 PORT 192.168.1.101:port的封包給 C,C分辨不出來,就會回資訊給B,B會忠實的再回給A1。A1就可以拿到別人的資料。

如何避免這個風險 ?
方法一: ftp server不要用21 port。但此時ftp client端的router要自己手動設portforwarding。
方法二: 使用安全的 tls + ftp 或 ftps,但perforamance會掉很多。

Reference:

https://wiki.filezilla-project.org/Network_Configuration#Malicious_routers.2C_firewalls_and_data_sabotage
http://tools.ietf.org/html/rfc2663
http://www.practicallynetworked.com/support/linksys_ftp_port.htm

2014年9月4日 星期四

[DNS] dynamic update

Dynamic update

環境:

開發機 (A)
裝DNS bind9的機器 (B)

在Zone data加入 allow-update 如下 (on B)

zone "test.com" {                      
        type master;                  
        file "/etc/zone/master/test.com";
        allow-transfer {any;};        
        allow-query {any;};            
        allow-update {any;};      <--- 以上三個設置都非常危險,這邊只是為了做實驗方便
};                                    

把rndc.key 從B copy到A (on B)

把record add到DNS Server (on A)

nsupdate -k rndc.key
> prereq yxdomain test.com
> update add trr123.test.com
> update add try123.test.com 300 A 192.253.253.16
> send

NOTE: 如果成功,可以看到 test.com.jnl會被產生出來

把.jnl file sync到zone file裡面 (on B)

針對test.com會自動sync並且砍.jnl file:
./rndc -k ../named/rndc.key sync -clean test.com也可以對全部的domain sync並砍掉所有的.jnl file
./rndc -k ../named/rndc.key sync -clean

NOTE: 這動作要定期做,以免.jnl的大小成長到超過2G。在很多平台上處理超過2G的file都會出問題。

參考資料:

http://docstore.mik.ua/orelly/networking_2ndEd/dns/ch10_02.htm
https://www.centos.bz/2012/05/bind-nsupdate-dynamic-dns-update-tool/
http://dns-learning.twnic.net.tw/bind/security.html
http://www.zytrax.com/books/dns/ch7/xfer.html#max-journal-size
ftp://ftp.isc.org/isc/bind/9.8.0-P4/doc/arm/Bv9ARM.ch04.html
http://serverfault.com/questions/560326/ddns-bind-and-leftover-jnl-files (最後一個post)

2014年9月2日 星期二

[login system] 如何實作 login system ?

環境:

A: Server (192.168.0.101) + Apache2 Server
B: Client (192.168.0.101) + browser

概念流程:

Cookie sid尚未存在於browser的情況 -

A  <--------------------------------------------- GET index.cgi -----------------------------------------  B
A  ---------------------------------------------- output login.html --------------------------------------> B
A  <------------------------------ POST username/passowrd  to login.cgi (js) -----------------------  B
A  -----after login.cgi response: write sid into cookie and then redirect to index.cgi (js)--------> B


Cookie sid存在browser的情況 -

A  <--------------------------------------------- GET index.cgi -----------------------------------------  B
A  ---------------------------------------------- output login.html --------------------------------------> B

index.cgi

if (COOKIE's sid in connected sessions) {
    output operation.html
} else {
    output login.html
}

login.cgi

if (username and password in db) {
    reponse sid for write cookie
} else {
    // do nothing
}

安全性考量:

1. user輸入的username和password都需要在client js端加密
2. 存下來的user/password一定要是加密後的結果

References:

一篇好文章,有關password加密入門基礎觀念
http://www.jasypt.org/howtoencryptuserpasswords.html

CGI programming
http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm

Http Cookie
http://en.wikipedia.org/wiki/HTTP_cookie

有關C/C++ key generate sample code(public key/private key)
https://shanetully.com/2012/04/simple-public-key-encryption-with-rsa-and-openssl/

有關javascript encrypt
https://code.google.com/p/crypto-js/#SHA-1


2014年8月30日 星期六

[Web] 如何在Ubuntu上架設Apache2 server ?

如何在Ubuntu上架設Apache2 server ?

環境:

192.168.0.101 (Ubuntun)

安裝:

sudo apt-get install apache2

啟用Cgi模組:

sudo a2enmod cgi

編輯Apache2 conf檔:

vim /etc/apache2/apache2.conf
----------------------------------------------------------------------
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
<Directory /var/www/cgi-bin/>
AddHandler cgi-script cgi
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>

重新啟動Apache2:

sudo service apache2 restart

撰寫Cgi程式: 

vim index.cpp
----------------------------------------------------------
#include <stdio.h>

int main(int argc, char *argv[]) {
        printf ("Content-type: text/html\n\n");
        printf ("<html>\n");

        printf ("\t<head>\n");
        printf ("\t\t<titLe>!!! Hello, world!!!</title>\n");
        printf ("\t</head>\n");
        printf ("\t<body>\n");
        printf ("\t\t<h1>hello~~~~~~~</h1>\n");
        printf ("\t</body>\n");
        printf ("</html>\n");
        return 0;
}

編譯:

vim Makefile
-------------------------------------------------------------
all:
        $(CXX) index.cpp -o index.cgi


make

移動Cgi程式:

將編譯好的.cgi放到 /var/www/cgi-bin/ 下面

Test:

http://192.168.0.101/cgi-bin/

Debug:

cat /var/log/apache2/access.log
cat /var/log/apache2/error.log

[git] 如何git push/clone 免打密碼 ?

如何git push/clone 免打密碼 ?


使用時機:

不想每次git push時都要打密碼

環境:

A (192.168.0.101) Ubuntu (開發機)
B (192.168.0.100) Synology DS + Synology Git Server

概念:

在A上把public key generate出來後放到B上,並且設定好ssh即可做到ssh免密碼登入。
因為git push/clone都是走ssh,所以就可以在push/clone時免打密碼。

SSH Key Generate: (on A)

ssh-keygen -t rsa (產生/root/.ssh/id_rsa 以及/root/.ssh/id_rsa.pub)
NOTE:注意當問"Enter passphrase (empty for no passphrase):"直接enter即可,否則還是會需要輸入一個passpharse

Copy public key to B: (on A)

scp /root/.ssh/id_rsa.pub 192.168.0.100:/root/.ssh/id_rsa.git.pub

Set default git user name: (on A)

vim /root/.gitconfig
----------------------------------------------
[user]
        name = root
                    ^^^^^ 必須和 B 上的git init --bare的目錄是同個owner

Concat content into authorized_keys: (on B)

cat /root/.ssh/id_rsa.git.pub >> /root/.ssh/authorized_keys

成功後就可以從A ssh到B、A git clone/push B的project而不用輸入密碼


[git] 如何使用Synology的git package建立自己的 git server ?

如何使用Synology的git package建立自己的 git server ?

使用時機:

不想用GitHub,想建立自己的git server時,開發環境就只有你自己一個人(root)會push code

環境:

A (192.168.0.101) Ubuntu (開發機)
B (192.168.0.100) Synology DS

開始: (on B)

去package center下載Git Server

配置: (on B)

Package方面完全不用配置,啟動Git Server即可
但要console進入DS然後在/volume1下建一個目錄,譬如: /volume1/git_exmaples

建立Project: (on B)

cd /volume1
mkdir project1
cd project1
git init --bare

抓Code: (on A)

git clone ssh://192.168.0.100:/volume1/git_examples/project1


接下來...就能在這個clone下來的project做任何git能做的事情 : )

[Working env] 如何使用 ssh + putty ?

如何使用 ssh + putty ?


使用時機:

在Winodows上想要連到你Ubuntu的開發機來開發程式。

環境:

A (192.168.0.101) Ubuntu
B (192.168.0.102) Windows


開始: (on A)

安裝ssh

sudo apt-get install ssh

啟動sshd

安裝完似乎會自己啟動的樣子

設定ssh可連線來源 

sudo vim /etc/hosts.allow
------------------------------------------
sshd:192.168.0.102:ALLOW

使用: (on B)

去 google搜尋 putty 以及使用方法

[Working env] 如何在Synology DS上開發自己的(c/c++)程式 ?

如何在Synology DS上開發自己的(c/c++)程式 ?

使用時機:

想在Synology DS上開發自己的binary。

環境:

A (192.168.0.101) Ubuntu
B (192.168.0.100) Synology cedarview 5.0

開始:

去 http://www.synology.com/#tool 的  
"Toolkits and GPL source" 依據你的平台(ex. cedarview) 下載開發包

準備:

把開發包 gcc463_glibc213_i686_cedarview-GPL.tgz 解壓縮
可以在 i686-pc-linux-gnu/bin下看到 i686-pc-linux-gnu-g++ 以及 i686-pc-linux-gnu-gcc

第一個Makefile環境:

vim Makefile
---------------------------------------------------------------
TOOL_PATH= [your deitectory]/i686-pc-linux-gnu

CXX=$(TOOL_PATH)/bin/i686-pc-linux-gnu-c++
CC=$(TOOL_PATH)/bin/i686-pc-linux-gnu-gcc

all:
        $(CXX) test.cpp


vim test.cpp
----------------------------------------------------------------
#include <stdio.h>

int main() {
        printf ("hello~~\n");
        return 0;

}

Run:

make;
./a.out

[Working Env] 如何在Ubuntu使用nfs mount ?

如何在Ubuntu使用nfs mount ?

使用時機:

開發機在A而目標機器在B,可以在B上mount A, 加速開發速度

環境:

A (192.168.0.101) Ubuntu
B (192.168.0.100) Synology cedarview 5.0

如何安裝nfs: (on A)

sudo apt-get install nfs-common
sudo apt-get nfs-kernel-server

如何啟動nfs: (on A)

vim /etc/exports
------------------------------------------------
     [directory path on A] 192.168.0.100(ro,sync)

/etc/init.d/nfs-kernel-server restart

如何mount directory: (on B)

mount 192.168.0.101:[directory path on A] [existed path on B]/.

2014年8月23日 星期六

懷孕前三個月要注意的事情

懷孕前三個月要注意的事情


1. 切勿勞累,最好是一直躺著就好,盡量不要走動 (會掉胎的)
2. 盡量吃比較營養的東西
3. 有些食物不能吃,請參考中醫有關懷孕的書
4. 最可怕的是不知道自己懷孕還去拔牙、喝酒、...

如何改變 Extjs 的 listview 的 cursor types

如何將 Extjs 的 listview 的 cursor types 改為 default且移除滑鼠移上去時的hight light效果

先說明什麼是 cursor style好了,

cursor syle是一個CSS的property。當指定 style="cursor:default;" 時就會是一般的滑鼠游標,當指定 style="cursor:pointer;" 時就會是一個小手圖示。
詳情可以參考以下:http://www.w3schools.com/cssref/pr_class_cursor.asp


在說明一下什麼是 listview 好了,listview是Extjs中的 grid panel 前身,也就是它的功能和 grid panel是類似的,並且在extjs 4.0已經被合併到 gird panel裡面了。如果你指定以下這樣的code:http://dev.sencha.com/deploy/ext-3.4.0/examples/view/list-view.html你發現當滑鼠游標移過去的時候,會出現'小手'。這是因為某一個 css 發揮作用,必然是有某一層 css 的 style 設為 "cursor:pointer;"且你會發現滑鼠遊標移過去的地方會自動有'光棒'的效果(或是說highlight的效果)


方法一

How to fix it ?

先說如何移除當滑鼠移過去會出現hight light的效果。
在 listview 有一個欄位為 overClass 就是用來做當滑鼠移上去時 hight light這件事情。
因此我們可以把 overClass設為 空字串 '' 即可以消除high light效果。

另外如果想要移除當滑鼠移過去會出現小手的效果,我目前找到的方法是使用extjs的 XTemplate。

因為 listview有一個 tpl的欄位專門來存 XTemplate的 obj,
我們可以先按 F12 開 javascript的 debug mode,然後觀察 listview就可以看到在
tpl下面有一個 html的 property。

仔細觀察就可以發現就是 listview 目前的 table 狀態。
我們修改這個 property 內容就可以達到我們任何想要的外觀。
這邊有一篇不錯的內容:
http://www.cnblogs.com/lipan/archive/2011/12/12/2274740.html
看完應該就可以知道XTemplate如何使用。

但我們要的其實很單純,就是修改cursor style。
因此我們可以在debug mode把listivew的tpl的html這個property 的內容 copy一分下來,並且修改style部分改為 "style=cursor:default;"就可以達成我們的需求。

進一步延伸 (XTemplate)

看完上面說的這個link之後
http://www.cnblogs.com/lipan/archive/2011/12/12/2274740.html

應該就可以發現使用XTemplate就可以不被extjs的框架限制住,而達到很大的彈性,做我們想做的任何行為。
而缺點就是在javascript內內嵌 html的 code,可能會有maintain性不好的疑慮。

會了XTemplate似乎可以做出很多有趣的元件出來,這部分不是很懂,下回待續吧 : )


方法二

How to fix it ?

覆寫 listview 的 cls property 譬如叫 'my-style-no-cursor'
...
cls: 'my-style-no-cursor'
..

然後定義自己的 .css 如下
.my-style-no-cursor .x-list-body dt {
    cursor: default;
}

詳細解說:

當覆寫了 listview 的 cls ,在 browser的debug mode我們就可以看到 listview 會把這個css加入它的div class 裡面

當我們定義了 .my-style-no-cursor .x-list-body dt 這樣子的 selector 就會將這樣的style正確的套用到每個 dt 上。(browser 先選擇 .my-style-no-cursor然後是 .x-list-body 最後 dt tag)

2014年7月2日 星期三

linux 上一次取代多個檔案的字串

如果我們想要把某個目錄下面的檔案的xxx字串取代為yyy,該如何做呢 ?

方法:

find * | xargs -i sed -i "s:xxx:yyy:g" {}

[git pull --rebae] It looks like git-am is in progress. Cannot rebase.

今天遇到一個問題,當我下 git pull --rebase 時會出現以下的error message:

     It looks like git-am is in progress. Cannot rebase.

解法;

rm -rf .git/rebase-apply

linux mail 夾帶檔案(attach file) 範例 : 使用 mutt

無法查看此摘要。請 按這裡查看文章。

2014年6月23日 星期一

如何用python建立可以吃POST資訊的 cgi server ?

老實說,我還真的不太會寫python以及cgi或是web相關的程式。

 藉著這次工作上要寫一個update的功能,就順便學習一下 :)

 首先在網路上找到下面這個: (server.py)
#!/usr/bin/env python

import BaseHTTPServer

import CGIHTTPServer

import cgitb; cgitb.enable()  ## This line enables CGI error reporting



server = BaseHTTPServer.HTTPServer

handler = CGIHTTPServer.CGIHTTPRequestHandler

server_address = ("", 8000)

handler.cgi_directories = ["/"]



httpd = server(server_address, handler)

httpd.serve_forever()

然後,又在網路上找到下面這個: (test_cgi.py)
!/usr/bin/env python
import cgi, cgitb
form = cgi.FieldStorage()

# Get data from fields
first_name = form.getvalue('first_name')
last_name  = form.getvalue('last_name')

print ("Content-type:text/html\r\n\r\n")
print ("<html>")
print ("<head>")
print ("<title>Hello - Second CGI Program</title>")
print ("</head>")
print ("<body>")
print ("<h2>Hello %s %s</h2>" % (first_name, last_name))
print ("</body>")
print ("</html>")

然後把這兩個東西放到你的網頁資料夾下,
以Ubuntu的apache2來說的話是在 /var/www 下。
稍為檢查一下權限,阿,不太會設,都給它755下去好了


chmod 755 server.py
chmod 755 test_cgi.py

接下來就run server.py
./server.py


喔太棒了,block住了,看來應該是有在跑。
開個chrome驗證一下好了:
http://192.168.21.15:8000/test_cgi.py

哦在網頁上看到

Hello None None


哈,對,最後一步來用wget來測一下吧 : )

wget --post-data "first_name=vincent&last_name=yang" http://192.168.21.15:8000/test_cgi.py


哈,得到這樣的 file

<html>

<head>

<title>Hello - Second CGI Program</title>

</head>

<body>

<h2>Hello vincent yang</h2>

</body>

</html>



看來我們已經完成自己的 cgi ,並且可以依據client傳過來的 POST 資料給予不同的回應。
接下來怎麼變化就有很多種玩法了。

2014年6月20日 星期五

git

以下介紹我常用的git指令:

# 抓server上的某個git project
git clone ssh://[username]@[server dns name]/[directory]/projectname.git

# 建立local branch
(branchname可以從 git la上拿到)
git co -b [branchname] origin/[branchname]

# 砍掉branch
git branch -d [branchname]

# 看目前local有哪些branch
git br

# 切換到某個branch
git co [branchname]

# 抓其他branch的commit到這個branch (傳說中的 cherry-pick)
git la //確定是哪一個commit拿到hashcode
git cherry-pick [hash code]

# 抓最新版的code
git pull
or
git pull --rebase

# 和local有conflict的處理方法
git stash
git pull --reabse
git stash pop

#加入某一個file
git add [filename]

#移除某一個 file
git reset [filename]

# 加入這一個目錄以下的所有files以及子目錄
git add .

# 加入這一個目錄以下的所有被修改的files以及子目錄下被修改的files(曾經被commit in的那些files)
git add -u .

# reset all(回到剛抓下來的狀態, 你的修改code會通通消失, 請小心使用)
git reset --hard

# 回到某一個版本
git reset --hard [hashcode]

# 拿到hash code
git la
or
git log

# commit in到local git database
git ci

# 做patch
(如果尚未add)
git df > patchfilename
(如果add了)
git df --cached > patchfilename
(如果commit了)
git show [hashcodename] > patchfilename

# apply patch
patch -p1 < patchfilename

# code review 比較兩個版本
(如果add了)
git difftool --cached -U10

# 修改尚未push commit in的註解
git ci --amend

# revert 之前 commit in的
git revert [hashcode]

# 上傳到git server
git push


結論:
我常用的大概就是這樣吧,理論上應該就足夠了吧 ?

2014年6月13日 星期五

[vim] tabe

主題:

如何同時開啟多個file並且以tabe分頁的方式開啟 ?


答案:

vim -p file1 file2


主題:

如何進入vim後tabe分頁的方式開啟其他file ?

答案:

:tabe file2