/* * Menu: Info Support > Find commented code * Kudos: Peter Hendriks * License: EPL 1.0 * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript */ function main() { loadBundle("org.eclipse.core.jobs"); var ProgressMonitorDialog = Packages.org.eclipse.jface.dialogs.ProgressMonitorDialog; var IRunnableWithProgress = Packages.org.eclipse.jface.operation.IRunnableWithProgress; var runnableWithProgress = new IRunnableWithProgress({ run: runCommentSearch }); new ProgressMonitorDialog(window.getShell()).run(true, true, runnableWithProgress); window.getActivePage().showView("org.eclipse.ui.views.TaskList"); } function runCommentSearch(monitor) { var files = resources.filesMatching(".*\\.java"); monitor.beginTask("Searching for commented code...", files.length); try { var match; for each( file in files ) { monitor.subTask(file.getEclipseObject().getName()); file.removeMyTasks(); var previousLineCodeComment = false; for each( line in file.lines ) { if (monitor.isCanceled()) { return; } if (match = line.string.match(/^.*\/\/.*[;{}]\s*$/)) { if (!previousLineCodeComment) { line.addMyTask("Commented code: " + match[0]); } previousLineCodeComment = true; } else { previousLineCodeComment = false; } } monitor.worked(1); } } finally { monitor.done(); } }
2011-12-21 19:03
[轉載] Aptana Scripting 一個背景處理的範例
轉載自:Monkeying with Eclipse | Info Support
分類:
轉載,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
0
回應
2011-12-21 18:58
Aptana Scripting - Find TODOs 範例中的參數
/* * Menu: Editors > Find TODOs * Kudos: Ingo Muschenetz * License: EPL 1.0 * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript */ function main() { var files = resources.filesMatching(".*\\.js"); var match; for each( file in files ) { file.removeMyTasks( ); for each( line in file.lines ) { if (match = line.string.match(/\/\/TODO: (.*)/)) { line.addMyTask( match[1] ); } } } window.getActivePage().showView("org.eclipse.ui.views.TaskList"); }
只列出當前文件的方法
var files = resources.filesMatching('.*/'+editors.activeEditor.textEditor.titleToolTip);
resources 部分的方法
filesMatching(".*\\.js") filesMatchingForProject("Project Name",".*\\.js") filesMatchingIgnoreCase(".*\\.js") filesMatchingForProjectIgnoreCase("Project Name",".*\\.js")
file 部分的方法
size lines removeMyTasks()
line 部分的方法
lineNumber string addMyTask('Task String')
2011-12-21 18:46
Aptana Scripting 學習筆記
- 在任何一個專案的頂層目錄,建立一個名稱為 scripts 或 monkey 的目錄
- 在此目錄下建立副檔名為 *.js 或 *.em 的 JavaScript 的文件
一個空白文件的內容如下:
/* * Menu: Samples > Execute Snippet * Key: M1+M2+M3+F * Kudos: Jax Hu * License: EPL 1.0 * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript */ function main(){ }
快捷鍵的代號對應:
M1 | Control/Command |
M2 | Shift |
M3 | Alt/Option |
一個可以用來顯示物件成員的函數:
/**顯示物件的成員 * @param {Object} val 物件 */ function var_dump(val){ var name, value, temp=[]; for (name in val) { try { value = (val[name]+''). replace("function",'<b style="color:#00f;">$&</b>'); }catch (e) { value = (e+'').fontcolor('red'); } temp.push('<b style="color:#707;">'+name+'</b> = '+value); } webView = views.getView("var_dump"); webView.showView(true); webView.setTitle('var_dump'); webView.setHTML(temp.join("<hr/>").fixed()); }
常用方法以及數值:
/*當前文件的位置 * => D:/WorkSpace/my_project/test.js */ location /*當前文件的名稱 * => test.js */ editors.activeEditor.title /*當前的文件內容*/ editors.activeEditor.source /*當前文件的 URI 位址*/ editors.activeEditor.uri /*儲存當前文件*/ editors.activeEditor.save(); /*對當前文件-開啟另存新檔的對話匡*/ editors.activeEditor.textEditor.doSaveAs() /*對當前文件的目錄路徑 * => D:/WorkSpace/my_project */ editors.activeEditor.textEditor.editorInput.file.parent.location /*目前開啟的所有文件*/ editors.all[]; /*儲存全部編輯器,傳入 true 會開啟存檔提示*/ window.workbench.saveAllEditors(false); /*重新開啟 Eclipse*/ window.workbench.restart(); /*關閉 Eclipse*/ window.workbench.close(); /* 開新的編輯器,可以透過這個開啟空白文件,或是已經存在的檔案, * 之後 editors.activeEditor 會轉到這個文件上 */ fileUtils.open('textile_to_redmine.txt'); /*取得當前的專案名稱*/ editors.activeEditor.textEditor.editorInput.file.project.name; /*取得所有的專案*/ Packages.org.eclipse.core.resources.ResourcesPlugin.workspace.root.projects; editors.activeEditor.textEditor.editorInput.file.workspace.root.projects
建立一個新的視圖,或開啟已存在的視圖:
webView = views.getView("my_view_name"); /*顯示視圖*/ webView.showView(true); /*設定標題*/ webView.setTitle("My View Title"); /*設定內容的HTML*/ webView.setHTML('<h1>OK</h1>'); /*或指定內容的網址*/ webView.url = "http://www.google.com"; webView.addEventListener("LocationChanging", function(event){ var location = event.innerEvent.location; // Print out the location to the Java console Packages.java.lang.System.out.println("You clicked on: " + location); });
替換選擇的文字區段:
/*選擇的起始位置*/ var starting = editors.activeEditor.selectionRange.startingOffset; /*選擇的結束位置*/ var ending = editors.activeEditor.selectionRange.endingOffset; /*選擇的文字內容*/ var text = editors.activeEditor.source.substring(starting, ending); /*文字跳脫處理,或其他自訂的處理*/ text = escape(text); /*替換選擇的文字*/ editors.activeEditor.applyEdit(starting, ending-starting, text); /*重新選擇文字區段*/ editors.activeEditor.selectAndReveal(starting, text.length);
檔案存取:
var file = new File("myFile.txt"); file.createNewFile(); file.write("Date: "); var text = file.readLines();
Web 資料請求的方式:
var req = new WebRequest(); req.open("GET", "http://xml.weather.yahoo.com/forecastrss?p=94103"); var text = req.send();
分類:
工作備忘,
筆記,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
2
回應
2011-12-21 15:05
[轉載] Aptana Scripting File.js Class Document
轉載自:Koders Code Search: File.js - JavaScript
/** * File.js * * Adding this file to your Active Libraries will give you code assist for the * Aptana Studio scripting engine. * * @author Kevin Lindsey * @version 1.0 */ /** * This object represents a file or a directory in the file system * * @constructor * @param {String} name * The relative or absolute path to a file or directory */ function File(name) {} /* * Properties * /** * Get the absolute path for this file or directory * * @type {String} Returns the absolute path to this file or directory */ File.prototype.absolutePath = ""; /** * Get the file's base name. This is the filename only without the extension. * If the file does not have an extension, then this will return the full * name * * @type {String} Returns this file's base name */ File.prototype.baseName = ""; /** * Determine if this file is readable * * @type {Boolean} Returns true if this File is readable */ File.prototype.canRead = false; /** * Determine if ths file is writable * * @type {Boolean} Returns true if this File is writable */ File.prototype.canWrite = false; /** * Determine if this file or directory exists in the file system * * @type {Boolean} Returns true if this File exists in the file system */ File.prototype.exists = false; /** * Returns the file extension of this File * * @type {String} Returns the last instance of "." and the text after it. * An empty string will be returned if no extension if found. The return * value includes the '.' */ File.prototype.extension = ""; /** * Determines if thie File is a file in the file system * * @type {Boolean} Returns true if this File is a file in the file system */ File.prototype.isFile = false; /** * Determines if this File is a directory in the file system * * @type {Boolean} Returns true if this File is a directory in the file * system */ File.prototype.isDirectory = false; /** * Returns a list of File objects for all files in the File. This is equivalent * to listing out all files in a directory * * @type {Array} Returns an array of File objects, one for each file and * directory in this File */ File.prototype.list = []; /** * Returns the file's name without path information * * @type {String} Returns the file's name */ File.prototype.name = ""; /** * Returns a new File object of this object's parent directory * * @type {File} Returns this file's parent File */ File.prototype.parentFile = {}; /** * Returns the character used to separate directories on the underlying OS * * @type {String} Returns the directory separator */ File.prototype.separator = ""; /* * Methods */ /** * Create a new file in the file system at the location specified by this * object * * @return {Boolean} Returns true if the file was created successfully. */ File.prototype.createNewFile = function() {}; /** * Return all lines from this File's text file * * @return {Array} Returns an array of strings, one for each line in the file. */ File.prototype.readLines = function() {}; //eof
分類:
轉載,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
0
回應
2011-12-21 15:05
[轉載] Aptana Scripting PrintStream.js Class Document
轉載自:Koders Code Search: PrintStream.js - JavaScript
/** * PrintStream.js * * Adding this file to your Active Libraries will give you code assist for the * Aptana Studio scripting engine. * * @author Kevin Lindsey * @version 1.0 */ /** * This object represents an underlying Java PrintStream * * @constructor * / function PrintStream() {} /* * Methods */ /** * Print the given text to the underlying stream * * @param {String} text * The text to send to the stream */ PrintStream.prototype.print = function(text) {}; /** * Print the given text to the underlying stream followed by end-of-line * * @param {String} text * The text to send to the stream */ PrintStream.prototype.println = function(text) {}; //eof
分類:
轉載,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
0
回應
2011-12-21 15:04
[轉載] Aptana Scripting Editor.js Class Document
轉載自:Koders Code Search: Editor.js - JavaScript
/** * Editor.js * * Adding this file to your Active Libraries will give you code assist for the * Aptana Studio scripting engine. * * @author Kevin Lindsey * @version 1.0 */ /** * Editor * * @constructor * @extends {EventTarget} */ function Editor() {} /* * Properties */ /** * Get/set the position of the cursor in this editor * * @type {Number} The current cursor offset position */ Editor.prototype.currentOffset = 0; /** * Get the File object that this editor is editing * * @type {File} Returns a File object or undefined */ Editor.prototype.file = {}; /** * Get the language MIME type for this editor * * @type {String} Returns this editors language type */ Editor.prototype.language = ""; /** * Get the lexemes associated with this editor * * @type {Array} Returns an array of Lexemes */ Editor.prototype.lexemes = []; /** * Get the line delimiter for this editor * * @type {String} Returns the editor's line terminator */ Editor.prototype.lineDelimiter = ""; /** * Get the source associated with this editor * * @type {String} Returns the source text in this editor */ Editor.prototype.source = ""; /** * Get the length of the source in this editor * * @type {Number} Returns the number of characters in this editor's document */ Editor.prototype.sourceLength = 0; /** * Get the number of columns in a tab * * @type {Number} Returns the number of spaces that equal one tab */ Editor.prototype.tabWidth = 0; /** * Get the zero-based line number of the line at the top of the editor * * @type {Number} The top-most line's index */ Editor.prototype.topIndex = 0; /** * Get/set the editor's word wrap setting. Setting this to true turns on word * wrapping. * * @type {Boolean} The word wrap setting. */ Editor.prototype.wordWrap = false; /* * Methods */ /** * Apply an edit to the current document. This function allows you to delete * and insert text in one operation, if desired. * * @param {Number} offset * The offset within the source where this edit is to take place * @param {Number} deleteLength * The number of characters to remove before inserting the new text * @param {String} insertText * The new text to insert at the given offset */ Editor.prototype.applyEdit = function(offset, deleteLength, insertText) {}; /** * Get the zero-based line number at the specified character offset * * @param {Number} offset * The character offset within the editor's document */ Editor.prototype.getLineAtOffset = function(offset) {}; /** * Scroll the editor to bring the current selection or caret position into * view. */ Editor.prototype.showSelection = function() {}; //eof
分類:
轉載,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
0
回應
2011-12-21 15:04
[轉載] Aptana Scripting Global.js Document
轉載自:Koders Code Search: Global.js - JavaScript
/** * Global.js * * Adding this file to your Active Libraries will give you code assist for the * Aptana Studio scripting engine. * * @author Kevin Lindsey * @version 1.0 */ /* * Properties */ /** * Retrieve the Editors object to access editors and editor events * * @type {Editors} Returns the global Editors object */ var editors = {}; /** * Retrieve the error print stream. * * @type {PrintStream} Returns the output stream used to display errors */ var err = {}; /** * This is a reference to the only instance of this object. All scripts run in * their own protected scope. However, this Global is accessible from all * scripts. Properties placed on "global" will be accessible to all scripts * * @type {Global} Returns a reference to the global scope */ var global = {}; /** * Retrieve the Menus object to access menus and menu events * * @type {Menus} Returns the global Menus object */ var menus = {}; /** * Retrieve the standard output print stream. * * @type {PrintStream} Returns the standard output stream */ var out = {}; /** * Retrieve the View object to access views and view events * * @type {Views} Returns the global Views object */ var views = {}; /* * Methods */ /** * Display an alert dialog with the given message * * @param {String} message * The message to display in the dialog */ var alert = function(message) {}; /** * Execute a string in the current shell. This is experimental and may be * removed in a future version of the scripting environment * * @param {String} command * The command to execute in the shell * @return {Object} Returns an object with the following properties: code, * stdout, stderr. Code is the return code from the command. Stdout * contains any text that was emitted to standard out while it was * executing. Likewise, stderr contains any errors that were emitted. */ var execute = function(command) {}; /** * Call Java's System.getProperty. * * @param {String} property * The name of the property to retrieve * @return {String} Returns the specified property value or the string * "undefined" if the property does not exist */ var getProperty = function(property) {}; /** * Include a JavaScript file into the current script's scope. this is used to * load dependent libraries into the script that invokes this function. * * @param {String} filename * The name of the file to include in the script */ var include = function(filename) {}; /** * Load a library into the scripting environment. Each script loaded with this * function will be assigned a unique ID and, if it exists, the init() function * will be invoked. This gives each script the ability to initialize itself and * to setup any event listeners it wishes to subscribe to. * * Each script will exist in its own scope; however, this Global is also * included in the scope chain. All variables and functions defined in the * script will not collide with any other scripts. * * Shared properties can be placed on the "global" property. All scripts loaded * via this function will then be able to see those properties. This can be * used to share data between scripts. * * @param {String} filename * The file system path to the script to load * @return {String} Returns a unique string identifier for the loaded script. * This identifier can be used later to invoke functions within the * script; however, this is more for internal use at this point. If the * script fails to load, this will return undefined. */ var loadBundle = function(filename) {}; //eof
分類:
轉載,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
0
回應
2011-12-21 15:04
[轉載] Aptana Scripting Editors.js Class Document
轉載自:Koders Code Search: Editors.js - JavaScript
/** * Editors.js * * Adding this file to your Active Libraries will give you code assist for the * Aptana Studio scripting engine. * * @author Kevin Lindsey * @version 1.0 */ /** * Editors * * @constructor */ function Editors(){} /* * Properties */ /** * Get all editors currently being displayed * * @type {Array} Returns an array of Editor instances, one for each open editor */ Editors.prototype.all = []; /** * Get the currently active editor * * @type {Editor} Returns the currently active editor or undefined if no editor is open */ Editors.prototype.activeEditor = {}; /* * Methods */ /** * Get the object that represents all editors of a given type. This is * typically used to register event handlers for a given event type for * all editors of a specified language. * * @param {String} type * The editor type to retrieve. Currently, this is the MIME type for the * language the editor supports. * @return {EditorType} Returns the editor type for the given MIME type or * undefined if no editor type exists for the given MIME type */ Editors.prototype.getEditorType = function(type) {}; /** * Open the specified filename in a new editor * * @param {String} filename * The name of the file to open in the file system * @return {Editor} Returns a new Editor object for the newly opened editor */ Editors.prototype.open = function(filename) {}; //eof
分類:
轉載,
Aptana,
Aptana Scripting,
Eclipse,
Eclipse Monkey,
JavaScript
0
回應
2011-12-21 14:32
[PHP] 兩種 while 的應用
滿足其中一個條件,就再來一次
滿足其中一個條件,就跳過之後的判斷,同樣的程式流,也可以在 function 中用 return 做到。
<?php $a = 'OK'; while(true){ if($a == "OK"){ var_dump('OK'); $a = "NO"; continue; }elseif($a == "NO"){ var_dump('NO'); $a = "XXX"; continue; }else{ var_dump('default'); } break; }
滿足其中一個條件,就跳過之後的判斷,同樣的程式流,也可以在 function 中用 return 做到。
<?php $a = 'OK'; do{ if($a == "OK"){ echo 'OK'; break; } if($a == "NO"){ echo 'NO'; break; } if($a == "Off"){ echo 'NO'; break; } }while(false);
2011-12-16 15:04
Sphinx 增量索引的方法
之前有寫過一篇 MySQL 全文檢索引擎 - Sphinx 的文章,最近又把它拿出來用了,不過當時即時更新索引的問題,如今則找到解決的方法了,透過更新增量索引的方式達到即時更新。
簡單的說就是利用兩個索引表的合併查詢來做到,一個是完整的索引表,一個是針對當日資料變動的增量索引。
部分的設定檔如下:
建立 SphinxSE 表
特別注意在 CONNECTION 中所指定索引表為 people。
SQL 的查詢測試
這裡使用 INNER JOIN 方式作查詢,這樣對於刪除資料的變動,就不會出現在查詢結果中。
透過 PHP 更新增量索引
在資料 INSERT 或 UPDATE 時,去呼叫索引更新,這樣在第一時間就可以更新索引。
如果 Server 是 Ubuntu,請記得在 vim /etc/sudoers 中賦予 apache 使用 indexer 的權限。
在 crontab 中加上排程
利用離峰時間來更新完整的索引表,由於刪除資料的變動需要更新完整索引表才有辦法移除。
簡單的說就是利用兩個索引表的合併查詢來做到,一個是完整的索引表,一個是針對當日資料變動的增量索引。
部分的設定檔如下:
# ... source _source_base { # 來源-共用的設定 } source people_full : _source_base { sql_query = \ SELECT \ people_profile.id, \ people_profile.main_name \ FROM people_profile sql_query_killlist = \ SELECT id FROM people_profile \ WHERE update_date >= CURDATE() } # 增量索引來源,這邊只會抓出當日變動的資料 # 建議在 update_date 欄位加上 MySQL INDEX source people_delta : people_full { sql_query = \ SELECT \ people_profile.id, \ people_profile.main_name \ FROM people_profile \ WHERE people_profile.update_date >= CURDATE() } index _index_base { # 索引-共用的設定 } index people_full : _index_base { source = people_full path = /var/lib/sphinxsearch/data/people_full } # 增量索引 index people_delta : _index_base { source = people_delta path = /var/lib/sphinxsearch/data/people_delta } # 透過 distributed 類型來合併索引 index people { type = distributed local = people_full local = people_delta } # ...
建立 SphinxSE 表
特別注意在 CONNECTION 中所指定索引表為 people。
CREATE TABLE people_sphinx( id BIGINT UNSIGNED NOT NULL COMMENT '搜尋結果的 Id', weight INT NOT NULL COMMENT '搜尋結果的權重', query VARCHAR(3072) NOT NULL COMMENT '搜尋的查詢條件', INDEX(query) )ENGINE=SPHINX CONNECTION="sphinx://localhost:9312/people" COMMENT='People Sphinx搜尋連接介面';
SQL 的查詢測試
這裡使用 INNER JOIN 方式作查詢,這樣對於刪除資料的變動,就不會出現在查詢結果中。
SELECT A.id, A.main_name, B.weight FROM people_sphinx B INNER JOIN people_profile A USING(id) WHERE B.query='馬丁尼茲;mode=any;limit=1000'
透過 PHP 更新增量索引
在資料 INSERT 或 UPDATE 時,去呼叫索引更新,這樣在第一時間就可以更新索引。
shell_exec('sudo indexer --quiet --rotate people_delta 2>&1');
如果 Server 是 Ubuntu,請記得在 vim /etc/sudoers 中賦予 apache 使用 indexer 的權限。
www-data ALL=(root) NOPASSWD: /usr/bin/indexer
在 crontab 中加上排程
利用離峰時間來更新完整的索引表,由於刪除資料的變動需要更新完整索引表才有辦法移除。
00 00 * * * indexer --quiet --rotate --all > /dev/null 2>&1
2011-12-16 13:01
[轉載][MySQL] Replication option binlog_format
轉載自:MYSQL5.1复制参数binlog_format
MySQL 5.1 中,在復制方面的改進就是引進了新的複制技術:基於行的複制。 簡言之,這種新技術就是關注表中發生變化的記錄,而非以前的照抄binlog 模式。 從MySQL 5.1.12 開始,可以用以下三種模式來實現:基於SQL語句的複制(statement-based replication, SBR),基於行的複制(row-based replication, RBR),混合模式複制(mixed-based replication, MBR)。 相應地,binlog的格式也有三種:STATEMENT,ROW,MIXED。 MBR 模式中,SBR 模式是默認的。
在運行時可以動態低改變binlog的格式,除了以下幾種情況:
如果binlog採用了MIXED 模式,那麼在以下幾種情況下會自動將binlog的模式由SBR 模式改成RBR 模式。
設定主從復制模式的方法非常簡單,只要在以前設定複製配置的基礎上,再加一個參數:
當然了,也可以在運行時動態修改binlog的格式。 例如
現在來比較以下SBR 和RBR 2中模式各自的優缺點
SBR 的優點:
SBR 的缺點:
RBR 的優點:
RBR 的缺點:
另外,針對系統庫 mysql 裡面的表發生變化時的處理規則如下:
注:採用RBR 模式後,能解決很多原先出現的主鍵重複問題
MySQL 5.1 中,在復制方面的改進就是引進了新的複制技術:基於行的複制。 簡言之,這種新技術就是關注表中發生變化的記錄,而非以前的照抄binlog 模式。 從MySQL 5.1.12 開始,可以用以下三種模式來實現:基於SQL語句的複制(statement-based replication, SBR),基於行的複制(row-based replication, RBR),混合模式複制(mixed-based replication, MBR)。 相應地,binlog的格式也有三種:STATEMENT,ROW,MIXED。 MBR 模式中,SBR 模式是默認的。
在運行時可以動態低改變binlog的格式,除了以下幾種情況:
- 存儲過程或者觸發器中間
- 啟用了NDB
- 當前會話試用RBR 模式,並且已打開了臨時表
如果binlog採用了MIXED 模式,那麼在以下幾種情況下會自動將binlog的模式由SBR 模式改成RBR 模式。
- 當DML語句更新一個NDB表時
- 當函數中包含UUID() 時
- 2個及以上包含AUTO_INCREMENT 字段的表被更新時
- 行任何INSERT DELAYED 語句時
- 用UDF 時
- 視圖中必須要求使用RBR 時,例如創建視圖是使用了UUID() 函數
設定主從復制模式的方法非常簡單,只要在以前設定複製配置的基礎上,再加一個參數:
binlog_format="STATEMENT" #binlog_format="ROW" #binlog_format="MIXED"
當然了,也可以在運行時動態修改binlog的格式。 例如
mysql> SET SESSION binlog_format = 'STATEMENT'; mysql> SET SESSION binlog_format = 'ROW'; mysql> SET SESSION binlog_format = 'MIXED'; mysql> SET GLOBAL binlog_format = 'STATEMENT'; mysql> SET GLOBAL binlog_format = 'ROW'; mysql> SET GLOBAL binlog_format = 'MIXED';
現在來比較以下SBR 和RBR 2中模式各自的優缺點
SBR 的優點:
- 歷史悠久,技術成熟
- binlog文件較小
- binlog中包含了所有數據庫更改信息,可以據此來審核數據庫的安全等情況
- binlog可以用於實時的還原,而不僅僅用於復制
- 主從版本可以不一樣,從服務器版本可以比主服務器版本高
SBR 的缺點:
- 不是所有的UPDATE語句都能被複製,尤其是包含不確定操作的時候。
- 調用具有不確定因素的UDF 時復制也可能出問題
- 使用以下函數的語句也無法被複製:
- LOAD_FILE()
- UUID()
- USER()
- FOUND_ROWS()
- SYSDATE() (除非啟動時啟用了--sysdate-is-now 選項)
- INSERT ... SELECT 會產生比RBR 更多的行級鎖
- 複製需要進行全表掃描(WHERE 語句中沒有使用到索引)的UPDATE 時,需要比RBR 請求更多的行級鎖
- 對於有AUTO_INCREMENT 字段的InnoDB表而言,INSERT 語句會阻塞其他INSERT 語句
- 對於一些複雜的語句,在從服務器上的耗資源情況會更嚴重,而RBR 模式下,只會對那個發生變化的記錄產生影響
- 存儲函數(不是存儲過程)在被調用的同時也會執行一次NOW() 函數,這個可以說是壞事也可能是好事
- 確定了的UDF 也需要在從服務器上執行
- 數據表必須幾乎和主服務器保持一致才行,否則可能會導致複製出錯
- 執行複雜語句如果出錯的話,會消耗更多資源
RBR 的優點:
- 任何情況都可以被複製,這對複制來說是最安全可靠的
- 和其他大多數數據庫系統的複制技術一樣
- 多數情況下,從服務器上的表如果有主鍵的話,複製就會快了很多
- 複製以下幾種語句時的行鎖更少:
- INSERT ... SELECT
- 包含AUTO_INCREMENT 字段的INSERT
- 沒有附帶條件或者並沒有修改很多記錄的UPDATE 或DELETE 語句
- 執行INSERT,UPDATE,DELETE 語句時鎖更少
- 從服務器上採用多線程來執行複製成為可能
RBR 的缺點:
- binlog 大了很多
- 複雜的回滾時binlog 中會包含大量的數據
- 主服務器上執行UPDATE 語句時,所有發生變化的記錄都會寫到binlog 中,而SBR 只會寫一次,這會導致頻繁發生binlog 的並發寫問題
- UDF 產生的大BLOB 值會導致複製變慢
- 無法從binlog 中看到都複製了寫什麼語句
- 當在非事務表上執行一段堆積的SQL語句時,最好採用SBR 模式,否則很容易導致主從服務器的數據不一致情況發生
另外,針對系統庫 mysql 裡面的表發生變化時的處理規則如下:
- 如果是採用INSERT,UPDATE,DELETE 直接操作表的情況,則日誌格式根據binlog_format 的設定而記錄
- 如果是採用GRANT,REVOKE,SET PASSWORD 等管理語句來做的話,那麼無論如何都採用SBR 模式記錄
注:採用RBR 模式後,能解決很多原先出現的主鍵重複問題
2011-12-16 12:41
[MySQL] Replication Master to Slave
Master上的設定
my.cnf
進入Master mysql
建立同步連接帳號 db_sync,密碼12345,給 slave_host,
鎖定資料庫寫入
取得 MASTER 上的 Log File 名稱與 Position 編號,這個編號代表當前的 Log 戳記
離開 mysql
匯出 MASTER 上的資料
解除鎖定
Slave上的設定
my.cnf
匯入資料
進入 Slave mysql
停止跟清除之前的 SLAVE 連接
設定Slave 與 Master 的連接
啟動 SLAVE 的連接
察看連接狀態
接著到 Master 上新增一筆資料測試看看吧!
參考文章:
MYSQL 主从服务器配置
mysql主从同步快速设置
MySQL 設定 Replication (Master – Slave)
多主一从mysql replication同步表的大胆尝试
MySQL复制的数据库过滤若干问题
MYSQL5.1复制参数binlog_format
my.cnf
# 主機ID,不可以重複,範圍 0~4294967295 server-id = 1 # Log 的名稱或路徑,只有名稱會將檔案放在 datadir 下 log_bin = mysql-bin # Log 的格式{ROW,STATEMENT,MIXED},最低支援版本 5.1.8 binlog_format = MIXED # 需要同步的資料庫,多個寫多行 binlog_do_db = test binlog_do_db = mydb # 針對 InnoDB 且有用 transaction 的設定 innodb_flush_log_at_trx_commit=1 sync_binlog=1
進入Master mysql
mysql -u root -p
建立同步連接帳號 db_sync,密碼12345,給 slave_host,
mysql> GRANT REPLICATION SLAVE ON *.* TO 'db_sync'
'slave_host' IDENTIFIED BY '12345';
鎖定資料庫寫入
mysql> FLUSH TABLES WITH READ LOCK;
取得 MASTER 上的 Log File 名稱與 Position 編號,這個編號代表當前的 Log 戳記
mysql> SHOW MASTER STATUS\G;
File: | mysql-bin.000010 |
---|---|
Position: | 106 |
Binlog_Do_DB: | test,mydb |
Binlog_Ignore_DB: |
離開 mysql
mysql> quit
匯出 MASTER 上的資料
mysqldump -uroot -p --flush-logs --opt --master-data test > test.sql
解除鎖定
mysql -u root -p -e UNLOCK TABLES;
Slave上的設定
my.cnf
# 主機ID,不可以重複,範圍 0~4294967295 server-id = 2 # 需要同步的資料庫,或指定的資料表 replicate_wild_do_table = test.% replicate_wild_do_table = mydb.category replicate_wild_do_table = mydb.blog # 與 Master 斷線後,重新嘗試連接的時間(sec) master-connect-retry = 60
匯入資料
mysql -uroot -p test < test.sql
進入 Slave mysql
mysql -uroot -p
停止跟清除之前的 SLAVE 連接
mysql> STOP SLAVE; RESET SLAVE;
設定Slave 與 Master 的連接
mysql> CHANGE MASTER TO MASTER_HOST='master_host', MASTER_USER='db_sync', MASTER_PASSWORD='12345', MASTER_LOG_FILE='mysql-bin.000011', MASTER_LOG_POS=106;
啟動 SLAVE 的連接
mysql> START SLAVE;
察看連接狀態
mysql> SHOW SLAVE STATUS\G;
Slave_IO_State: | Connecting to master |
Master_Host: | master_host |
Master_User: | db_sync |
Master_Port: | 3306 |
Connect_Retry: | 60 |
Master_Log_File: | mysql-bin.000011 |
Read_Master_Log_Pos: | 106 |
Relay_Log_File: | master_host-relay-bin.000011 |
Relay_Log_Pos: | 4 |
Relay_Master_Log_File: | mysql-bin.000011 |
Slave_IO_Running: | Yes |
---|---|
Slave_SQL_Running: | Yes |
Replicate_Do_DB: | |
Replicate_Ignore_DB: | |
Replicate_Do_Table: | |
Replicate_Ignore_Table: | |
Replicate_Wild_Do_Table: | test.%,mydb.category,mydb.blog |
Replicate_Wild_Ignore_Table: | |
Last_Errno: | 0 |
Last_Error: | |
Skip_Counter: | 0 |
Exec_Master_Log_Pos: | 106 |
Relay_Log_Space: | 106 |
Until_Condition: | None |
Until_Log_File: | |
Until_Log_Pos: | 0 |
Master_SSL_Allowed: | No |
Master_SSL_CA_File: | |
Master_SSL_CA_Path: | |
Master_SSL_Cert: | |
Master_SSL_Cipher: | |
Master_SSL_Key: | |
Seconds_Behind_Master: | 0 |
接著到 Master 上新增一筆資料測試看看吧!
參考文章:
MYSQL 主从服务器配置
mysql主从同步快速设置
MySQL 設定 Replication (Master – Slave)
多主一从mysql replication同步表的大胆尝试
MySQL复制的数据库过滤若干问题
MYSQL5.1复制参数binlog_format
2011-12-16 06:49
[PHP] 縮圖方式的比較
- imageCopyResized 速度快,但縮圖的品質很粗糙
- imageCopyResampled 縮圖的品質好,但花費的時間有點多
優點是指需要安裝 GD 套件,這個套件不管是在 Windows 或 Linux 上很容易找到跟安裝,在處理圖片的類型明確跟尺寸不大的情況下,使用這兩個函數是不錯的。
為了改善 imageCopyResampled 效率,可以利用 imageCopyResized 做預先縮圖,例如要縮圖的大小為 100*100 時,可以先將圖片縮小成四倍或八倍,如 400*400 或 800*800,可改善超大圖造成的效率不好。
- ImageMagick + Imagick 使用外部的程式來處理圖片,使用指令的方式或透過 Imagick 套件來處理縮圖。
優點是支援下面多種格式,效率快且沒有記憶體錯誤的問題。
3FR, A, AI, ART, ARW, AVI, AVS, B, BGR, BGRA, BMP, BMP2, BMP3, BRF, BRG, C, CAL, CALS, CAPTION, CIN, CIP, CLIP, CMYK, CMYKA, CR2, CRW, CUR, CUT, DCM, DCR, DCX, DDS, DFONT, DJVU, DNG, DOT, DPX, EPDF, EPI, EPS, EPS2, EPS3, EPSF, EPSI, EPT, EPT2, EPT3, ERF, EXR, FAX, FITS, FRACTAL, FTS, G, G3, GBR, GIF, GIF87, GRADIENT, GRAY, GRB, GROUP4, HALD, HISTOGRAM, HRZ, HTM, HTML, ICB, ICO, ICON, INFO, INLINE, IPL, ISOBRL, J2C, JNG, JP2, JPC, JPEG, JPG, JPX, K, K25, KDC, LABEL, M, M2V, M4V, MAP, MAT, MATTE, MIFF, MNG, MONO, MOV, MP4, MPC, MPEG, MPG, MRW, MSL, MSVG, MTV, MVG, NEF, NULL, O, ORF, OTB, OTF, PAL, PALM, PAM, PATTERN, PBM, PCD, PCDS, PCL, PCT, PCX, PDB, PDF, PDFA, PEF, PES, PFA, PFB, PFM, PGM, PGX, PICON, PICT, PIX, PJPEG, PLASMA, PNG, PNG24, PNG32, PNG8, PNM, PPM, PREVIEW, PS, PS2, PS3, PSB, PSD, PTIF, PWP, R, RADIAL-GRADIENT, RAF, RAS, RBG, RGB, RGBA, RGBO, RLA, RLE, SCR, SCT, SFW, SGI, SHTML, SR2, SRF, STEGANO, SUN, SVG, SVGZ, TEXT, TGA, THUMBNAIL, TIFF, TIFF64, TILE, TIM, TTC, TTF, TXT, UBRL, UIL, UYVY, VDA, VICAR, VID, VIFF, VST, WBMP, WMF, WMV, WMZ, WPG, X, X3F, XBM, XC, XCF, XPM, XPS, XV, XWD, Y, YCbCr, YCbCrA, YUV
2011-12-15 18:51
[PHP] Documentor (phpdoc) 備忘
Linux 安裝指令
sudo pear install -o PhpDocumentor
Windows 安裝指令
C:\wamp\bin\php\php5.2.4\pear.bat install -o PhpDocumentor
使用方式
phpdoc 參數說明
中文亂碼的問題
新版的 phpdoc 輸出的格式已經是 UTF-8 了,所以只要在目錄下增加一個 .htaccess 文件,然後內容為:
參考文件:
多采多姿的程式筆記: phpDocumentor筆記 - 0 立即體驗
phpDocumentor Tutorial
phpDocumentor学习记录
sudo pear install -o PhpDocumentor
Windows 安裝指令
C:\wamp\bin\php\php5.2.4\pear.bat install -o PhpDocumentor
使用方式
phpdoc --parseprivate \ --output HTML:frames:earthli \ --ignore Smarty/ \ --directory /var/www/lib,/var/www/data_mod,/var/www/etc \ --target /var/www/docs
phpdoc 參數說明
-f | --filename | 要解析的檔案名稱,可使用 ‘,’分隔多個檔案 “file1,file2”,可以包含完整路徑和使用 * ? 萬用符號。 |
-d | --directory | 要解析的目錄路徑,可使用 ‘,’分隔多個目錄路徑 “directory1,directory2”。 |
-t | --target | 指定要輸出的目錄。 |
-i | --ignore | 要忽略的檔案名稱,可使用 ‘,’分隔多個檔案 “file1,file2”,可以使用 * ? 萬用符號。 |
-is | --ignoresymlinks | 忽略系統連結的檔案或目錄,預設是關閉的。 |
-it | --ignore-tags | 忽略解析的標籤。 @package, @subpackage, @access, @ignore 可能是無法忽視。 |
-q | --quiet | 不顯示解析/轉換的訊息,在 cron 排程時可以選擇開啟,預設是關閉的。 |
-ti | --title | 產生出的文件的標題,預設為 ‘Generated Documentation’。 |
-h | --help | 顯示幫助訊息。 |
-pp | --parseprivate | 將私有(private)成員函式或私有變數也都加入程式文件裡。不然產生出的文件裡只會有公開(public)和保護(protected)的成員函式和變數。 |
-o | --output | 設置輸出文件的格式 |
| ||
-j | --javadocdesc | 相容 JavaDoc 的格式,預設是關閉的 |
中文亂碼的問題
新版的 phpdoc 輸出的格式已經是 UTF-8 了,所以只要在目錄下增加一個 .htaccess 文件,然後內容為:
AddCharset UTF-8 .html
參考文件:
多采多姿的程式筆記: phpDocumentor筆記 - 0 立即體驗
phpDocumentor Tutorial
phpDocumentor学习记录
2011-12-14 21:02
[轉載] Linux Rsync 參數說明
轉載自: Rsync 中文參數說明
內容開始
rsync 資料同步敘述:
rsync和rcp的模式很相似,不過rsync有需多參數可以使用來加速檔案傳送.
rsync遠端更新協定(remote-update protocol)可以透過網路來傳輸兩端檔案不同的地方.語法:
rsyncd 的設定檔:
說明:
參數:
內容開始
rsync 資料同步敘述:
rsync和rcp的模式很相似,不過rsync有需多參數可以使用來加速檔案傳送.
rsync遠端更新協定(remote-update protocol)可以透過網路來傳輸兩端檔案不同的地方.語法:
rsync [OPTION]... SRC [SRC]... DEST
rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST
rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST
rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST
rsync [OPTION]... [USER@]HOST:SRC [DEST]
rsync [OPTION]... [USER@]HOST::SRC [DEST]
rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]
rsyncd 的設定檔:
/usr/local/etc/rsyncd.conf
說明:
[ohaha]: | rsync區段的設定名稱 |
path: | 備份資料存放的路徑 |
auth users: | 連結rsync服務的帳號 |
uid,gid: | 採用何種身分進行檔案存取 |
secerts file: | 帳號密碼檔位置 |
read only: | 是否唯讀 |
參數:
-h, | --help | 顯示rsync求助資訊. |
--version | 顯示rsync版本. | |
-v, | --verbose | 複雜的輸出訊息. |
-q, | --quiet | 安靜模式,幾乎沒有訊息產生.常用在以cron執行rsync. |
-I, | --ignore-times | 通常rsync為了加快速度會忽略同樣檔案大小且同樣存取時間點的檔案. 可以透過此參數關閉此快速檢查. |
--size-only | rsync只檢查檔案大小是否改變,不管時間存取點是否改變. 通常用在mirror,且對方時間不太正確時. | |
-c, | --checksum | 在傳送之前透過128bit的md4檢查碼來檢查所有要傳送的檔案.(會拖慢速度.) |
-a, | --archive | archive mode 權限保存模式,相當於 -rlptgoD 參數. 很快速的保存幾乎所有的權限設定,除了硬式連結(透過-H設定). |
-r, | --recursive | 複製所有下層的資料(遞迴) |
-R, | --relative | 使用相對路徑. 如: rsync foo/bar/foo.c remote:/tmp/ 在遠端產生/tmp/foo.c檔案 rsync -R foo/bar/foo.c remote:/tmp/ 在遠端產生/tmp/foo/bar/foo.c 檔案 |
-R, | --relative | 不使用相對路徑. |
-b, | --backup | 目的地端先前已經存在的檔案在傳輸或刪除前會被備份. |
--backup-dir=DIR | 設定備份的資料夾. | |
--suffix=SUFFIX | 指定備份的檔案名稱字尾形式(預設為~). | |
-K, | --keep-dirlinks | 接收方將連結到資料夾的檔案視為資料夾處理 |
-l, | --links | 複製所有的連結 |
-H, | --hard-links | 保留硬式連結 |
-p, | --perms | 保留檔案權限 |
-o, | --owner | 保留檔案擁有者(root only) |
-g, | --group | 保留檔案群組 |
-D, | --devices | 保留device資訊(root only) |
-t, | --times | 保留時間點 |
-n, | --dry-run | 不實際執行傳送,只顯示將會有的傳輸動作 |
-S, | --sparse | 嘗試去處理稀疏的檔案,讓這些檔案在目的端佔去較少的磁碟空間. |
-W, | --whole-file | 複製所有的檔案,不額外作檢查. |
--no-whole-file | 關閉 --whole-file 參數 | |
-x, | --one-file-system | 不要跨越檔案系統分界(只在一個檔案系統處理) |
-B, | --block-size=SIZE | 強制透過rsync程式去比對修復block-sizeforce |
-e | --rsh=COMMAND | 定義所使用的remote shell |
--rsync-path=PATH | 定義rsync在遠端機器存放資料的路徑 | |
--existing | 只比對更新目的端已經存在的檔案 | |
--ignore-existing | 忽略目的端已經存在的檔案(也就是不更新) | |
--delete | 刪除傳送端已經不存在,而目的端存在的檔案 | |
--delete-excluded | 除了把傳送端已經不存在,而目的端存在的檔案刪除之外, 也刪除 --exclude 參數所包含的檔案. | |
--delete-after | rsync預設會在檔案傳送前進行相關刪除動作確保接收端有足夠的檔案空間, 但可以透過 --delete-after 讓刪除動作在檔案傳送後再行刪除. | |
--ignore-errors | 忽略任何錯誤既使是I/O error 也進行 --delete 刪除動作. | |
--max-delete=NUM | 定義rsync不要刪除超過 NUM 個檔案. | |
--partial | rsync若遇到傳輸過程中斷時,會把那些已經傳輸的檔案刪除. 在某種狀況下保留那些部分傳送的檔案是令人高興的. 你可以透過 --partial 參數達到這個目的. | |
--partial-dir=DIR | 在 --partial 參數啟動時,你還可以定義rsync把那些部分傳送的檔案 寫入定義的資料夾,而非直接寫入目的端.需要注意的是, 此資料夾不應該被其他使用者可以寫入.(如:/tmp) | |
--force | 當目的端資料夾被傳送端非資料夾名稱覆蓋時,強制rsync刪除資料夾, 即使該資料夾不是空的. | |
--numeric-ids | 不將傳送端檔案的uid及gid值,與目的端的使用者/群組進行配對. 若傳送端並沒有uid及gid的對應名稱(如:原帳號群組被刪除的遺留檔案), 或目的端沒有相對應的帳號/群組,保留數字型態的uid/gid | |
--timeout=TIMEOUT | 設定 I/O 逾時的時間(秒). 超過這個秒數而沒有資料傳送,rsync將會結束. 預設為0,也就是沒有定義逾時時間. | |
-T, | --temp-dir=DIR | 定義rsync在接收端產生暫時性的複製檔案時使用資料夾暫存. 預設是直接在接收端資料夾直接產生暫存檔案. |
--compare-dest=DIR | 定義rsync在目的端建立資料夾來比對傳送過來的檔案. | |
--link-dest=DIR | 與 --compare-dest 相同,但同時會針對無法改變的檔案建立硬式連結. | |
-z, | --compress | 壓縮模式,當資料在傳送到目的端進行檔案壓縮. |
-P | -P參數和 --partial --progress 相同.只是為了把參數簡單化. | |
-C, | --cvs-exclude | 排除那些通常不希望傳送的檔案.定義的方式與CVS傳送相同: RCS SCCS CVS CVS.adm RCSLOG cvslog.* tags TAGS .make.state .nse_depinfo *~ *$ *.old *.bak *.BAK *.orig *.rej *.a *.olb *.o *.obj *.so *.exe *.Z *.elc *.ln core .svn/ 符合以上條件的都會被忽略而不傳送. |
--exclude=PATTER | 符合PATTERN(規則表示式)樣式的檔案不進行傳送 | |
--exclude-from=FILE | 和--exclude參數相同,不過是把不進行傳送的檔案事先寫入某一檔案. 執行時,透過此參數讓rsync讀取. (; #開頭的行列或空白行會被rsync忽略) | |
--include=PATTERN | 定義rsync不要排除符合pattern樣式的檔案. | |
--include-from=FILE | 和--include參數相同,只不過把要包含的檔案寫到某一檔案. | |
--files-from=FILE | 把要傳送的檔案名稱都精確的寫入某一檔案,讓rsync讀取. 如: rsync -a --files-from=/tmp/foo /usr remote:/backup | |
-0 | --from0 | 定義檔案所要讀取的檔案是null字元結尾. |
--version | 顯示版本訊息. | |
--daemon | 定義rsync以daemon型態執行. | |
--no-detach | 當以daemon型態執行時,不要進行分裂且變成背景程序. | |
--address=ADDRESS | 定義所要連結(bind)的ip位址或是host名稱(daemon限定) | |
--config=FILE | 定義所要讀取的設定檔rsyncd.conf位置(daemon限定) 預設值為 /usr/local/etc/rsyncd.conf | |
--port=PORT | 定義rsyncd(daemon)要執行的port(預設為tcp 873) | |
--blocking-io | 使用blocking I/O連結遠端的shell,如rsh , remsh | |
--no-blocking-io | 使用non-blocking連結遠端的shell,如ssh (預設值) | |
--stats | 顯示檔案傳送時的資訊狀態 | |
--progress | 顯示傳送的進度.(給檔案傳送時,怕無聊的人用的..) | |
--log-format=FORMAT | 定義log的格式(在rsyncd.conf設定) | |
--password-file=FILE | 從檔案讀取與遠端rsync伺服器連結的密碼 | |
--bwlimit=KBPS | 定義傳輸頻寬的大小(KBytes/秒) | |
--write-batch=FILE | 把紀錄資料寫入一個檔案(給其他相同環境且相同需求的機器使用) | |
--read-batch=FILE | 透過讀取紀錄檔案來進行傳輸.(檔案由 --write-batch 參數產生) | |
--checksum-seed=NUM | 定義檔案 checksum-seed 的大小(byte) | |
-4 | --ipv4 | 使用IPv4協定 |
-6 | --ipv6 | 使用IPv6協定 |
2011-12-14 16:22
[PHP] 取得用戶真實 IP
轉載自:精進你的程式碼 - 從取得用戶端 IP 的函式談起
<?php function get_client_ip(){ foreach (array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ) as $key) { if(!array_key_exists($key, $_SERVER)){ continue; } foreach (explode(',', $_SERVER[$key]) as $ip) { $ip = trim($ip); if ((bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE )){ return $ip; } } } return null; }
2011-12-14 15:26
[Ubuntu] 掛載 Windows 分享目錄
- 安裝 smbfs 套件
sudo apt-get install smbfs
- 建立掛載目錄
sudo mkdir /mnt/windows_share
- 開放最大權限
sudo chmod 777
- 測試掛載
sudo mount -t cifs //192.168.0.10/my_share /mnt/windows_share -o username=jax,password=1234,noacl,noperm,codepage=cp950,iocharset=big5,file_mode=0666,dir_mode=0777
- 察看是否成功
df -h
Filesystem Size Used Avail Use% Mounted on
//192.168.0.10/my_share 7.9G 208K 7.9G 1% /mnt/windows_share - 卸載
sudo umount /mnt/windows_share
- 修改開機掛載設定檔
sudo vim /etc/fstab
/mnt/windows_share was on //192.168.0.10/my_share //192.168.0.10/my_share /mnt/windows_share cifs username=jax,password=1234,noacl,noperm,codepage=cp950,iocharset=big5,file_mode=0666,dir_mode=0777 0 0
- 套用開機設定檔
mount -a
- 再次察看是否成功
df -h
Filesystem Size Used Avail Use% Mounted on
//192.168.0.10/my_share 7.9G 208K 7.9G 1% /mnt/windows_share
參考資料:
在 ubuntu 中 mount windows 分享的目錄
載掛(Mount)遠端資料夾的應用
ubuntu的几个技巧: 自动连接Windows共享文件夹, 定时文件同步备份
訂閱:
文章 (Atom)